/* dummy calls when XRDP_FUSE is not defined */ int xfuse_init(void)
{ return0;
} int xfuse_deinit(void)
{ return0;
} int xfuse_check_wait_objs(void)
{ return0;
} int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout)
{ return0;
} int xfuse_create_share(tui32 device_id, constchar *dirname)
{ return0;
} void xfuse_delete_share(tui32 share_id) {} int xfuse_clear_clip_dir(void)
{ return0;
} int xfuse_file_contents_range(int stream_id, constchar *data, int data_bytes)
{ return0;
} int xfuse_file_contents_size(int stream_id, int file_size)
{ return0;
} int xfuse_add_clip_dir_item(constchar *filename, int flags, int size, int lindex)
{ return0;
}
/* FUSE_USE_VERSION must be defined globally for other parts of *xrdp-chansrvwhichinclude<fuse_lowlevel.h>fordefinitions.Check
* it's actually defined here */ #ifndef FUSE_USE_VERSION #error Define FUSE_USE_VERSION in the make system and recompile #endif
/* Check for FUSE features we may wish to use * *NotethatFUSE_VERSIONmightbemoreusefulforsomefeaturesthan *FUSE_USE_VERSION
*/ #if FUSE_VERSION >= FUSE_MAKE_VERSION(3,7) #define FUSE_SET_LOG_FUNC_AVAILABLE #endif
externstruct config_chansrv *g_cfg; /* in chansrv.c */
/* Local utility functions */
staticinlinechar *
_fuse_mount_name_colon_char_replace(constchar *dirname)
{ char *newdirname = (char *) dirname; if (g_cfg->fuse_mount_name_colon_char_replacement != ':')
{
newdirname = g_strdup(dirname); if (newdirname == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Failed to duplicate fuse mount name string"); return (char *) dirname;
} char *colonptr = g_strrchr(newdirname, ':'); if (colonptr != NULL)
{
*colonptr = g_cfg->fuse_mount_name_colon_char_replacement;
}
} return newdirname;
}
/* End of Local utility functions*/
/* Type of buffer used for fuse_add_direntry() calls */ struct dirbuf1
{ char buf[4096];
size_t len;
};
/* *Recordtypeusedtomaintainstatewhenrunningadirectoryscan
*/ struct state_dirscan
{
fuse_req_t req; /* Original FUSE request from opendir */ struct fuse_file_info fi; /* File info struct passed to opendir */
fuse_ino_t pinum; /* inum of parent directory */
};
/* *Recordtypeusedtomaintainstatewhenrunninganentrylookup
*/ struct state_lookup
{
fuse_req_t req; /* Original FUSE request from lookup */
fuse_ino_t pinum; /* inum of parent directory */ char name[XFS_MAXFILENAMELEN + 1]; /* Name to look up */
fuse_ino_t existing_inum; /* inum of an existing entry */
tui32 existing_generation; /* generation of the above */
};
/* *Recordtypeusedtomaintainstatewhenrunninganentrysetattr
*/ struct state_setattr
{
fuse_req_t req; /* Original FUSE request from lookup */
fuse_ino_t inum; /* inum of entry */ struct file_attr fattr; /* File attributes to set */
tui32 change_mask; /* Attributes to set in fattr */
};
/* *Recordtypeusedtomaintainstatewhenrunninganopen
*/ struct state_open
{
fuse_req_t req; /* Original FUSE request from lookup */ struct fuse_file_info fi; /* File info struct passed to open */
fuse_ino_t inum; /* inum of file to open */
};
/* *Recordtypeusedtomaintainstatewhenrunningacreate
*/ struct state_create
{
fuse_req_t req; /* Original FUSE request from lookup */ struct fuse_file_info fi; /* File info struct passed to open */
fuse_ino_t pinum; /* inum of parent directory */ char name[XFS_MAXFILENAMELEN + 1]; /* Name of file in parent directory */
mode_t mode; /* Mode of file to create */
};
/* *Recordtypeusedtomaintainstatewhenrunningaread
*/ struct state_read
{
fuse_req_t req; /* Original FUSE request from lookup */
};
/* *Recordtypeusedtomaintainstatewhenrunningawrite
*/ struct state_write
{
fuse_req_t req; /* Original FUSE request from lookup */
fuse_ino_t inum; /* inum of file we're writing */
};
/* *Recordtypeusedtomaintainstatewhenrunningaremove
*/ struct state_remove
{
fuse_req_t req; /* Original FUSE request from lookup */
fuse_ino_t inum; /* inum of file we're removing */
};
/* *Recordtypeusedtomaintainstatewhenrunningarename
*/ struct state_rename
{
fuse_req_t req; /* Original FUSE request from lookup */
fuse_ino_t pinum; /* inum of parent of file */
fuse_ino_t new_pinum; /* inum of new parent of file */ char name[XFS_MAXFILENAMELEN + 1]; /* New name of file in new parent dir */
};
/* *Recordtypeusedtomaintainstatewhenrunningaclose
*/ struct state_close
{
fuse_req_t req; /* Original FUSE request from lookup */ struct fuse_file_info fi; /* File info struct passed to open */
fuse_ino_t inum; /* inum of file to open */
};
/* *Recordtypeusedtomaintainstatewhenrunningastatfs
*/ struct state_statfs
{
fuse_req_t req; /* Original FUSE request from statfs */
};
struct xfuse_handle
{
tui32 DeviceId;
tui32 FileId; int is_loc_resource; /* this is not a redirected resource */
/* a directory handle, if this xfuse_handle represents a directory. *NULL,ifthisxfuse_handlerepresentsafile. * *Note:whenthisxfuse_handlerepresentsadirectory,thentheother *fieldsofthisstructurecontaininvalidvalues.
*/ struct xfs_dir_handle *dir_handle;
}; typedefstruct 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;
};
staticstruct list *g_req_list = 0; staticstruct xfs_fs *g_xfs; /* an inst of xrdp file system */ static ino_t g_clipboard_inum; /* inode of clipboard dir */ staticstruct fuse_lowlevel_ops g_xfuse_ops; /* setup FUSE callbacks */ staticint g_xfuse_inited = 0; /* true when FUSE is inited */ staticstruct fuse_session *g_se = 0; // For the below, see the source for the fuse_session_loop() function staticstruct fuse_buf g_buffer =
{
.mem = NULL
};
/* this is not a callback, but it's used by xfuse_cb_readdir() */ staticint xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b,
XFS_INODE *xinode, off_t offset);
/* Whether to create a dir of file depends on whether S_IFDIR is set in the
mode field */ staticvoid xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent, constchar *name, mode_t mode, struct fuse_file_info *fi);
/* if already inited, just return */ if (g_xfuse_inited)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "already inited"); return0;
}
/* This feature may be disabled */ if (!g_cfg->enable_fuse_mount)
{ /* *Onlylogthe'disabledmounts'messageonetime
*/ staticint disabled_mounts_msg_shown = 0; if (!disabled_mounts_msg_shown)
{
LOG(LOG_LEVEL_INFO, "FUSE mounts are disabled by config");
disabled_mounts_msg_shown = 1;
} return1;
}
if (g_se != 0)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "g_se is not zero"); return -1;
}
load_fuse_config();
/* define FUSE mount point */ if (g_cfg->fuse_mount_name[0] == '/')
{ /* String is an absolute path to the mount point containing
* %u or %U characters */
format_user_info(g_fuse_root_path, sizeof(g_fuse_root_path),
g_cfg->fuse_mount_name);
} else
{ /* mount_name is relative to $HOME, e.g. ~/xrdp_client,
* or ~/thinclient_drives */ unsignedint len = g_snprintf(g_fuse_root_path, sizeof(g_fuse_root_path), "%s/", g_getenv("HOME")); if (len < sizeof(g_fuse_root_path))
{
format_user_info(g_fuse_root_path + len, sizeof(g_fuse_root_path) - len, g_cfg->fuse_mount_name);
}
}
/* Remove all trailing '/' from the root path */
p = g_fuse_root_path + g_strlen(g_fuse_root_path); while ( p > g_fuse_root_path && *(p - 1) == '/')
{
--p;
*p = '\0';
}
/* This shouldn't happen */ if (g_strlen(g_fuse_root_path) == 0)
{
LOG(LOG_LEVEL_ERROR, "Fuse root path is empty after removing trailing '/'"); return -1;
}
/* if FUSE mount point does not exist, create it */ if (!g_directory_exist(g_fuse_root_path))
{
(void)g_create_path(g_fuse_root_path); if (!g_create_dir(g_fuse_root_path))
{
LOG(LOG_LEVEL_ERROR, "mkdir %s failed (%s)",
g_fuse_root_path, g_get_strerror()); return -1;
}
}
/* Get the characteristics of the parent directory of the FUSE mount
* point. Used by xfuse_path_in_xfuse_fs() */
g_fuse_root_parent_dev = -1;
g_fuse_root_parent_ino = -1;
p = (char *)g_strrchr(g_fuse_root_path, '/'); if (p != NULL)
{ /* Temporarily finish the root path at this point */
*p = '\0';
g_fuse_root_path_basename = p + 1;
g_fuse_root_parent_dev = g_file_get_device_number(g_fuse_root_path);
g_fuse_root_parent_ino = g_file_get_inode_num(g_fuse_root_path);
*p = '/';
}
if (g_fuse_root_parent_dev == -1 || g_fuse_root_parent_ino == -1)
{
LOG(LOG_LEVEL_ERROR, "Unable to obtain characteristics of directory containing %s",
g_fuse_root_path); return -1;
}
/* setup xrdp file system */ if (xfuse_init_xrdp_fs())
{ return -1;
}
int
xfuse_add_clip_dir_item(constchar *filename, int flags, int size, int lindex)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: filename=%s flags=%d size=%d lindex=%d",
filename, flags, size, lindex);
int result = -1;
if (g_xfs == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "xfuse_add_clip_dir_item() called with no filesystem");
} else
{ /* add entry to xrdp_fs */
XFS_INODE *xinode = xfs_add_entry( g_xfs,
g_clipboard_inum, /* parent inode */
filename,
(0666 | S_IFREG)); if (xinode == NULL)
{
LOG(LOG_LEVEL_INFO, "failed to create file %s in xrdp filesystem", filename);
} else
{
xinode->size = size;
xinode->lindex = lindex;
result = 0;
}
}
if (!xfs_get(g_xfs, fip->pinum))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "inode %ld is not valid", fip->pinum);
} elseif ((strcmp(name, ".") == 0) ||
(strcmp(name, "..") == 0))
{
; /* filename is . or .. - don't add it */
} else
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "parent_inode=%ld name=%s", fip->pinum, name);
/* Does the file already exist ? If it does it's important we *don'tmesswithit,aswe'reonlyenumeratingthedirectory,and *wedon'twanttodisruptanyexistingoperationsonthefile
*/
xinode = xfs_lookup_in_dir(g_xfs, fip->pinum, name); if (xinode == NULL)
{ /* Add a new node to the file system */
LOG_DEVEL(LOG_LEVEL_DEBUG, "Creating name=%s in parent=%ld in xrdp_fs",
name, fip->pinum);
xinode = xfs_add_entry(g_xfs, fip->pinum, name, fattr->mode); if (xinode == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "xfs_add_entry() failed");
} else
{
xinode->size = fattr->size;
xinode->atime = fattr->atime;
xinode->mtime = fattr->mtime; /* Initially, set the attribute change time to the file data
change time */
xinode->ctime = fattr->mtime;
if (IoStatus != STATUS_SUCCESS)
{ switch (IoStatus)
{ case STATUS_SHARING_VIOLATION: /* This can happen when trying to read the attributes of
* some system files (e.g. pagefile.sys) */ case STATUS_ACCESS_DENIED:
fuse_reply_err(fip->req, EACCES); break;
case STATUS_UNSUCCESSFUL: /* Happens if we try to lookup an illegal filename (e.g.
* one with a '*' in it) */
fuse_reply_err(fip->req, ENOENT); break;
case STATUS_NO_SUCH_FILE: /* Remove our copy, if any */ if (fip->existing_inum &&
(xinode = xfs_get(g_xfs, fip->existing_inum)) != NULL &&
xinode->generation == fip->existing_generation)
{
xfs_remove_entry(g_xfs, fip->existing_inum);
}
fuse_reply_err(fip->req, ENOENT); break;
/* Does the file already exist ? */
xinode = xfs_lookup_in_dir(g_xfs, fip->pinum, fip->name); if (xinode != NULL)
{ /* Is the existing file the same type ? */ if ((xinode->mode & (S_IFREG | S_IFDIR)) ==
(file_info->mode & (S_IFREG | S_IFDIR)))
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "inode=%ld name=%s already exists in xrdp_fs as %ld",
fip->pinum, fip->name, xinode->inum); if (xfs_get_file_open_count(g_xfs, xinode->inum) > 0)
{ /* *Don'tmesswithopenfiles.Thelocalattributesare *almostcertainlymoreup-to-date.Aworstcasescenario *wouldbetruncatingafilewe'recurrentlywriting *to,asthelookupvalueforthesizeisstale.
*/
LOG_DEVEL(LOG_LEVEL_DEBUG, "inode=%ld is open - " "preferring local attributes", xinode->inum);
} else
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "Updating attributes of inode=%ld", xinode->inum);
update_inode_file_attributes(file_info, TO_SET_ALL, xinode);
}
} else
{ /* Type has changed from file to directory, or vice-versa */
LOG_DEVEL(LOG_LEVEL_DEBUG, "inode=%ld name=%s of different type in xrdp_fs" " - removing",
fip->pinum, xinode->name);
xfs_remove_entry(g_xfs, xinode->inum);
xinode = NULL;
}
}
if (xinode == NULL)
{ /* Add a new node to the file system */
LOG_DEVEL(LOG_LEVEL_DEBUG, "Creating name=%s in parent=%ld in xrdp_fs",
fip->name, fip->pinum);
xinode = xfs_add_entry(g_xfs, fip->pinum, fip->name,
file_info->mode); if (xinode == NULL)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "xfs_add_entry() failed");
} else
{
xinode->size = file_info->size;
xinode->atime = file_info->atime;
xinode->mtime = file_info->mtime; /* Initially, set the attribute change time to the file data
change time */
xinode->ctime = file_info->mtime; /* device_id is inherited from parent */
}
} if (xinode != NULL)
{
make_fuse_entry_reply(fip->req, xinode);
} else
{
fuse_reply_err(fip->req, EIO);
}
}
if (IoStatus != STATUS_SUCCESS)
{ switch (IoStatus)
{ case STATUS_SHARING_VIOLATION: /* This can happen when trying to read the attributes of
* some system files (e.g. pagefile.sys) */ case STATUS_ACCESS_DENIED:
fuse_reply_err(fip->req, EACCES); break;
case STATUS_UNSUCCESSFUL: /* Happens if we try to lookup an illegal filename */ case STATUS_NO_SUCH_FILE:
fuse_reply_err(fip->req, ENOENT); break;
if (IoStatus != 0)
{ switch (IoStatus)
{ case STATUS_ACCESS_DENIED:
fuse_reply_err(fip->req, EACCES); break;
case STATUS_OBJECT_NAME_INVALID: case STATUS_OBJECT_NAME_NOT_FOUND:
fuse_reply_err(fip->req, ENOENT); break;
default:
fuse_reply_err(fip->req, EIO); break;
}
} else
{ if ((fip->mode & S_IFREG) != 0)
{ /* We've created a regular file */ /* Allocate an XFUSE_HANDLE for future file operations */ if ((fh = xfuse_handle_create()) != NULL)
{ /* save file handle for later use */
fh->DeviceId = DeviceId;
fh->FileId = FileId;
fip->fi.fh = xfuse_handle_to_fuse_handle(fh);
}
}
if ((fip->mode & S_IFREG) != 0 && fh == NULL)
{ /* We failed to allocate a file handle */
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(fip->req, ENOMEM);
} else
{
XFS_INODE *xinode; /* create entry in xrdp file system */
xinode = xfs_add_entry(g_xfs, fip->pinum, fip->name, fip->mode); if (xinode == NULL)
{ /* It's possible xfs_add_entry() has failed, as the *filehasalreadybeenadded(byalookuprequest) *inthetimebetweenourrequestandtheresponse *Thiscanhappenifan'ls'ishappeninginthesame *directoryasthisfileisbeingcreated. *
* We'll check for this before we fail the create */ if ((xinode = xfs_lookup_in_dir(g_xfs,
fip->pinum,
fip->name)) != NULL)
{ /* *Themodeshouldbecorrectanyway,butwe'll *setittothemoderequestedatcreation
*/
xinode->mode = fip->mode;
}
}
if (xinode == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Out of memory!");
fuse_reply_err(fip->req, ENOMEM);
} else
{
/**
* This routine is caused as a result of a file open request */ void xfuse_devredir_cb_open_file(struct state_open *fip, enum NTSTATUS IoStatus,
tui32 DeviceId, tui32 FileId)
{
XFUSE_HANDLE *fh;
if (IoStatus != 0)
{ switch (IoStatus)
{ case STATUS_ACCESS_DENIED:
fuse_reply_err(fip->req, EACCES); break;
case STATUS_OBJECT_NAME_INVALID: case STATUS_OBJECT_NAME_NOT_FOUND:
fuse_reply_err(fip->req, ENOENT); break;
default:
fuse_reply_err(fip->req, EIO); break;
}
} else
{ /* Allocate an XFUSE_HANDLE for future file operations */ if ((fh = xfuse_handle_create()) == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(fip->req, ENOMEM);
} else
{ /* save file handle for later use */
fh->DeviceId = DeviceId;
fh->FileId = FileId;
if ((wpath = g_strdup(path)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "system out of memory");
} else
{ /* Look ahead in the string for the basename of the FUSE mount point */ for (p = wpath;
(p = g_strstr(p, g_fuse_root_path_basename)) != NULL ;
p = p + 1)
{ /* Is this string preceded by a '/' ? */ if (p == wpath || *(p - 1) != '/')
{ continue;
}
/* Is the string followed by a '/' or '\0' ? We know it's *validtolookaheadthisfarinthestring,asg_strstr()
* tells us it is */ if (*(p + blen) != '/' && *(p + blen) != '\0')
{ continue;
}
/* Temporarily terminate the string early, and check the
* file system characteristics of the preceding directory */
*(p - 1) = '\0';
/* if ino is not valid, just return */ if ((xino = xfs_get(g_xfs, ino)) == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "inode %ld is not valid", ino);
fuse_reply_err(req, ENOENT);
} else
{
make_fuse_attr_reply(req, xino);
}
}
/* *Trytoaddtheentry.Fromthedocsforfuse_add_direntry():- *"Bufferneedstobelargeenoughtoholdtheentry.Ifit'snot, *thentheentryisnotfilledinbutthesizeoftheentryis *stillreturned."
*/
len = fuse_add_direntry(req,
&b->buf[b->len], /* index where new entry will be added to buf */ sizeof(b->buf) - b->len, /* Space left */
xinode->name, /* name of entry */
&stbuf, /* file attributes */
offset /* offset of next entry */
); if (len + b->len <= sizeof(b->buf))
{ /* Entry fitted in OK */
b->len += len;
result = 1;
}
/* On the first call, check the inode is valid */ if (off == 0 && !xfs_get(g_xfs, ino))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "inode %ld is not valid", ino);
fuse_reply_err(req, ENOENT);
} elseif ((xhandle = xfuse_handle_from_fuse_handle(fi->fh)) == NULL
|| (dh = xhandle->dir_handle) == NULL)
{ /* something seriously wrong somewhere! */
fuse_reply_buf(req, 0, 0);
} else
{
b.len = 0;
off_t new_off = off; while ((xinode = xfs_readdir(g_xfs, dh, &new_off)) != NULL)
{ if (xfuse_dirbuf_add1(req, &b, xinode, new_off) == 0)
{ break; /* buffer is full */
} /* Make sure we get the next entry next time */
off = new_off;
}
if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL)
{ /* dir already exists, just return it */
make_fuse_entry_reply(req, xinode);
} else
{ /* dir does not exist, create it */
xfuse_create_dir_or_file(req, parent, name, mode | S_IFDIR, NULL);
}
}
if (strlen(name) > XFS_MAXFILENAMELEN)
{
fuse_reply_err(req, ENAMETOOLONG);
} elseif ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "did not find file with pinode=%ld name=%s", parent, name);
fuse_reply_err(req, ENOENT);
}
elseif ((xinode->mode & S_IFDIR) != 0 &&
!xfs_is_dir_empty(g_xfs, xinode->inum))
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "cannot rmdir; directory is not empty");
fuse_reply_err(req, ENOTEMPTY);
}
elseif (!xinode->is_redirected)
{ /* specified file is a local resource */ //XFUSE_HANDLE *fh;
LOG_DEVEL(LOG_LEVEL_DEBUG, "LK_TODO: this is still a TODO");
fuse_reply_err(req, EROFS);
} else
{ /* specified file resides on redirected share */ struct state_remove *fip = g_new0(struct state_remove, 1); char *full_path = xfs_get_full_path(g_xfs, xinode->inum); if (!full_path || !fip)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(req, ENOMEM);
free(fip);
} else
{ constchar *cptr;
fip->req = req;
fip->inum = xinode->inum;
/* we want path minus 'root node of the share' */
cptr = filename_on_device(full_path);
/* get devredir to open the remote file * *Ifthiscallsucceeds,furtherrequestprocessinghappensin *xfuse_devredir_cb_rmdir_or_file()
*/ if (devredir_rmdir_or_file(fip, xinode->device_id, cptr))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "failed to send devredir_rmdir_or_file() cmd");
fuse_reply_err(req, EREMOTEIO);
free(fip);
}
elseif (!old_xinode->is_redirected)
{ /* specified file is a local resource */
LOG_DEVEL(LOG_LEVEL_DEBUG, "LK_TODO: this is still a TODO");
fuse_reply_err(req, EROFS);
}
else
{ /* resource is on a redirected share */ struct state_rename *fip = g_new0(struct state_rename, 1); char *old_full_path = xfs_get_full_path(g_xfs, old_xinode->inum); char *new_full_path = get_name_for_entry_in_parent(new_parent,
new_name);
if (!old_full_path || !new_full_path || !fip)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(req, ENOMEM);
free(fip);
free(old_full_path);
free(new_full_path);
} else
{ constchar *cptr; constchar *cp;
LOG_DEVEL(LOG_LEVEL_DEBUG, "write %zd bytes at off %lld to inode=%ld",
size, (longlong) off, ino);
if ((fh = xfuse_handle_from_fuse_handle(fi->fh)) == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "file handle fi->fh is NULL");
fuse_reply_err(req, EINVAL);
} elseif (fh->is_loc_resource)
{ /* target file is in .clipboard dir */
LOG_DEVEL(LOG_LEVEL_DEBUG, "THIS IS STILL A TODO!");
fuse_reply_err(req, EROFS);
} else
{ /* target file is on a remote device */
fusep = g_new0(struct state_write, 1); if (fusep == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(req, ENOMEM);
} else
{
fusep->req = req;
fusep->inum = ino;
if (change_mask == 0)
{ /* No changes have been made */
make_fuse_attr_reply(req, xinode);
} elseif (!xinode->is_redirected)
{ /* Update the local fs */
update_inode_file_attributes(&attrs, change_mask, xinode);
make_fuse_attr_reply(req, xinode);
} else
{ struct state_setattr *fip = g_new0(struct state_setattr, 1); char *full_path = xfs_get_full_path(g_xfs, ino); if (!full_path || !fip)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
fuse_reply_err(req, ENOMEM);
free(fip);
free(full_path);
} else
{ constchar *cptr;
fip->req = req;
fip->inum = ino; /* Save the important stuff so we can update our node if the
* remote update is successful */
fip->fattr = attrs;
fip->change_mask = change_mask;
/* we want path minus 'root node of the share' */
cptr = filename_on_device(full_path);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.