/** * 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 #include #include #include "arch.h" #include "chansrv_fuse.h" /* dummy calls when XRDP_FUSE is not defined */ int xfuse_init() { return 0; } int xfuse_deinit() { 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) {} 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 #include #include #include #include #include #include #include #include #include "arch.h" #include "os_calls.h" #include "chansrv_fuse.h" #include "list.h" #include "fifo.h" #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(); static int xfuse_deinit_xrdp_fs(); static int xfuse_init_lib(struct fuse_args *args); static int xfuse_is_inode_valid(int ino); // LK_TODO #if 0 static void xfuse_create_file(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, int type); #endif static void xfuse_dump_fs(); static void xfuse_dump_xrdp_inode(struct xrdp_inode *xino); static tui32 xfuse_get_device_id_for_inode(tui32 ino, char *full_path); static void fuse_reverse_pathname(char *full_path, char *reverse_path); static struct xrdp_inode * xfuse_get_inode_from_pinode_name(tui32 pinode, const char *name); static struct xrdp_inode * xfuse_create_file_in_xrdp_fs(tui32 device_id, int pinode, char *name, int type); static 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(); 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"); /* 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; free(handle); 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); } } /** *****************************************************************************/ 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 */