3114 lines
84 KiB
C
3114 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 .clipbard and share mounts
|
|
* o fix the HACK where I have to use my own buf instead of g_buffer
|
|
* this is in func xfuse_check_wait_objs()
|
|
* o if fuse mount point is already mounted, I get segfault
|
|
* o in open, check for modes such as O_TRUNC, O_APPEND
|
|
* o copying over an existing file does not work
|
|
* o after a dir is created, the device cannot be unmounted on the client side
|
|
* so something is holding it up
|
|
* o in thunar, when I move a file by dragging to another folder, the file
|
|
* is getting copied instead of being moved
|
|
* o unable to edit files in vi
|
|
* o fuse ops to support
|
|
* o touch does not work
|
|
* o chmod must work
|
|
* o cat >> file is not working
|
|
*
|
|
*/
|
|
|
|
//#define USE_SYNC_FLAG
|
|
|
|
static char g_fuse_mount_name[256] = "xrdp_client";
|
|
|
|
/* 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;
|
|
};
|
|
|
|
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, tui32 Length, tui64 Offset);
|
|
|
|
int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId);
|
|
|
|
/* forward declarations for FUSE callbacks */
|
|
static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name);
|
|
|
|
static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi);
|
|
|
|
/* this is not a callback, but its's used by xfuse_cb_readdir() */
|
|
static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b,
|
|
const char *name, fuse_ino_t ino);
|
|
|
|
static int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b,
|
|
const char *name, fuse_ino_t ino);
|
|
|
|
static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
off_t off, struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, mode_t mode);
|
|
|
|
static void xfuse_cb_rmdir(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name);
|
|
|
|
static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name);
|
|
|
|
static void xfuse_cb_rename(fuse_req_t req,
|
|
fuse_ino_t old_parent, const char *old_name,
|
|
fuse_ino_t new_parent, const char *new_name);
|
|
|
|
/* this is not a callback, but it is used by the above two functions */
|
|
static void xfuse_remove_dir_or_file(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, int type);
|
|
|
|
static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, mode_t mode,
|
|
struct fuse_file_info *fi, int type);
|
|
|
|
static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_release(fuse_req_t req, fuse_ino_t ino, struct
|
|
fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
off_t off, struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
|
|
size_t size, off_t off, struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, mode_t mode,
|
|
struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
struct fuse_file_info *fi);
|
|
|
|
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);
|
|
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 flilename 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));
|
|
|
|
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 */
|