Created
January 28, 2020 01:29
-
-
Save derekschrock/2775cb850f96c981d9e55580132781da to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* xrdp: A Remote Desktop Protocol server. | |
* | |
* 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. | |
* | |
* This file implements the interface in chansrv_fuse_fs.h | |
*/ | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#if defined(HAVE_CONFIG_H) | |
#include <config_ac.h> | |
#endif | |
#include "os_calls.h" | |
#include "chansrv_xfs.h" | |
/* | |
* Skip this module if FUSE is not supported. A standards-compliant C | |
* translation unit must contain at least one declaration (C99:6.9), and we've | |
* fulfilled that requirement by this stage. | |
*/ | |
#ifdef XRDP_FUSE | |
#define INODE_TABLE_ALLOCATION_INITIAL 4096 | |
#define INODE_TABLE_ALLOCATION_GRANULARITY 100 | |
/* inum of the delete pending directory */ | |
#define DELETE_PENDING_ID 2 | |
/* | |
* A double-linked list of inodes, sorted by inum | |
* | |
* The elements in the list are sorted in increasing inum order, as this | |
* allows a directory enumeration to be easily resumed if elements | |
* are removed or added. See xfs_readdir() for details on this. | |
*/ | |
typedef struct xfs_inode_all XFS_INODE_ALL; | |
typedef struct xfs_list | |
{ | |
XFS_INODE_ALL *begin; | |
XFS_INODE_ALL *end; | |
} XFS_LIST; | |
/* | |
* A complete inode, including the private elements used by the | |
* implementation | |
*/ | |
typedef struct xfs_inode_all | |
{ | |
XFS_INODE pub; /* Public elements */ | |
/* | |
* Directory linkage elements | |
* | |
* Because we don't support hard-linking, elements can be stored in | |
* one and only one directory:- | |
*/ | |
struct xfs_inode_all *parent; /* Parent inode */ | |
struct xfs_inode_all *next; /* Next entry in parent */ | |
struct xfs_inode_all *previous; /* Previous entry in parent */ | |
XFS_LIST dir; /* Directory only - children */ | |
/* | |
* Other private elements | |
*/ | |
unsigned int open_count; /* Regular files only */ | |
} XFS_INODE_ALL; | |
/* the xrdp file system in memory | |
* | |
* inode_table allows for O(1) access to any file based on the inum. | |
* Index 0 is unused, so we can use an inode of zero for | |
* an invalid inode, and avoid off-by-one errors index | |
* 1 is our '.' directory. | |
* 2 is the delete pending directory, where we can place | |
* inodes with a positive open count which are | |
* deleted. | |
* free_list List of free inode numbers. Allows for O(1) access to | |
* a free node, provided the free list is not empty. | |
*/ | |
struct xfs_fs | |
{ | |
XFS_INODE_ALL **inode_table; /* a table of entries; can grow. */ | |
fuse_ino_t *free_list; /* Free inodes */ | |
unsigned int inode_count; /* Current number of inodes */ | |
unsigned int free_count; /* Size of free_list */ | |
unsigned int generation; /* Changes when an inode is deleted */ | |
}; | |
/* A directory handle | |
* | |
* inum inum of the directory being scanned | |
* generation Generation of the inum we opened | |
*/ | |
struct xfs_dir_handle | |
{ | |
fuse_ino_t inum; | |
tui32 generation; | |
}; | |
/* module based logging */ | |
#define LOG_ERROR 0 | |
#define LOG_INFO 1 | |
#define LOG_DEBUG 2 | |
#ifndef LOG_LEVEL | |
#define LOG_LEVEL LOG_ERROR | |
#endif | |
#define log_error(_params...) \ | |
{ \ | |
g_write("[%10.10u]: XFS %s: %d : ERROR: ", \ | |
g_time3(), __func__, __LINE__); \ | |
g_writeln (_params); \ | |
} | |
#define log_always(_params...) \ | |
{ \ | |
g_write("[%10.10u]: XFS %s: %d : ALWAYS: ", \ | |
g_time3(), __func__, __LINE__); \ | |
g_writeln (_params); \ | |
} | |
#define log_info(_params...) \ | |
{ \ | |
if (LOG_INFO <= LOG_LEVEL) \ | |
{ \ | |
g_write("[%10.10u]: XFS %s: %d : ", \ | |
g_time3(), __func__, __LINE__); \ | |
g_writeln (_params); \ | |
} \ | |
} | |
#define log_debug(_params...) \ | |
{ \ | |
if (LOG_DEBUG <= LOG_LEVEL) \ | |
{ \ | |
g_write("[%10.10u]: XFS %s: %d : ", \ | |
g_time3(), __func__, __LINE__); \ | |
g_writeln (_params); \ | |
} \ | |
} | |
/* ------------------------------------------------------------------------ */ | |
static int | |
grow_xfs(struct xfs_fs *xfs, unsigned int extra_inodes) | |
{ | |
int result = 0; | |
unsigned int new_count = xfs->inode_count + extra_inodes; | |
XFS_INODE_ALL **new_table; | |
fuse_ino_t *new_free_list; | |
new_table = (XFS_INODE_ALL **) | |
realloc(xfs->inode_table, new_count * sizeof(new_table[0])); | |
if (new_table != NULL) | |
{ | |
unsigned int i; | |
for (i = xfs->inode_count ; i < new_count ; ++i) | |
{ | |
new_table[i] = NULL; | |
} | |
xfs->inode_table = new_table; | |
new_free_list = (fuse_ino_t *) | |
realloc(xfs->free_list, | |
new_count * sizeof(new_free_list[0])); | |
if (new_free_list) | |
{ | |
/* Add the new inodes in to the new_free_list, so the lowest | |
* number is allocated first | |
*/ | |
i = new_count; | |
while (i > xfs->inode_count) | |
{ | |
new_free_list[xfs->free_count++] = --i; | |
} | |
xfs->free_list = new_free_list; | |
xfs->inode_count = new_count; | |
result = 1; | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
static void | |
add_inode_to_list(XFS_LIST *list, XFS_INODE_ALL *xino) | |
{ | |
fuse_ino_t inum = xino->pub.inum; | |
/* Find the element we need to insert after */ | |
XFS_INODE_ALL *predecessor = list->end; | |
while (predecessor != NULL && predecessor->pub.inum > inum) | |
{ | |
predecessor = predecessor->previous; | |
} | |
if (predecessor == NULL) | |
{ | |
/* Inserting at the beginning */ | |
/* Set up links in node */ | |
xino->next = list->begin; | |
xino->previous = NULL; | |
/* Set up back-link to node */ | |
if (list->begin == NULL) | |
{ | |
/* We are the last node */ | |
list->end = xino; | |
} | |
else | |
{ | |
list->begin->previous = xino; | |
} | |
/* Set up forward-link to node */ | |
list->begin = xino; | |
} | |
else | |
{ | |
/* Set up links in node */ | |
xino->next = predecessor->next; | |
xino->previous = predecessor; | |
/* Set up back-link to node */ | |
if (predecessor->next == NULL) | |
{ | |
list->end = xino; | |
} | |
else | |
{ | |
predecessor->next->previous = xino; | |
} | |
/* Set up forward-link to node */ | |
predecessor->next = xino; | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
static void | |
remove_inode_from_list(XFS_LIST *list, XFS_INODE_ALL *xino) | |
{ | |
if (xino->previous == NULL) | |
{ | |
/* First element */ | |
list->begin = xino->next; | |
} | |
else | |
{ | |
xino->previous->next = xino->next; | |
} | |
if (xino->next == NULL) | |
{ | |
/* Last element */ | |
list->end = xino->previous; | |
} | |
else | |
{ | |
xino->next->previous = xino->previous; | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
static void | |
link_inode_into_directory_node(XFS_INODE_ALL *dinode, XFS_INODE_ALL *xino) | |
{ | |
xino->parent = dinode; | |
add_inode_to_list(&dinode->dir, xino); | |
} | |
/* ------------------------------------------------------------------------ */ | |
static void | |
unlink_inode_from_parent(XFS_INODE_ALL *xino) | |
{ | |
remove_inode_from_list(&xino->parent->dir, xino); | |
xino->next = NULL; | |
xino->previous = NULL; | |
xino->parent = NULL; | |
} | |
/* ------------------------------------------------------------------------ */ | |
struct xfs_fs * | |
xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid) | |
{ | |
struct xfs_fs *xfs = g_new0(struct xfs_fs, 1); | |
XFS_INODE_ALL *xino1 = NULL; | |
XFS_INODE_ALL *xino2 = NULL; | |
if (xfs != NULL) | |
{ | |
xfs->inode_count = 0; | |
xfs->free_count = 0; | |
xfs->inode_table = NULL; | |
xfs->free_list = NULL; | |
xfs->generation = 1; | |
if (!grow_xfs(xfs, INODE_TABLE_ALLOCATION_INITIAL) || | |
(xino1 = g_new0(XFS_INODE_ALL, 1)) == NULL || | |
(xino2 = g_new0(XFS_INODE_ALL, 1)) == NULL) | |
{ | |
free(xino1); | |
free(xino2); | |
xfs_delete_xfs_fs(xfs); | |
xfs = NULL; | |
} | |
else | |
{ | |
/* | |
* The use of grow_xfs to allocate the inode table will make | |
* inodes 0, 1 (FUSE_ROOT_ID) and 2 (DELETE_PENDING_ID) the first | |
* available free inodes. We can ignore these */ | |
xfs->free_count -= 3; | |
xfs->inode_table[0] = NULL; | |
xfs->inode_table[FUSE_ROOT_ID] = xino1; | |
xfs->inode_table[DELETE_PENDING_ID] = xino2; | |
xino1->pub.inum = FUSE_ROOT_ID; | |
xino1->pub.mode = (S_IFDIR | 0777) & ~umask; | |
xino1->pub.uid = uid; | |
xino1->pub.gid = gid; | |
xino1->pub.size = 0; | |
xino1->pub.atime = time(0); | |
xino1->pub.mtime = xino1->pub.atime; | |
xino1->pub.ctime = xino1->pub.atime; | |
strcpy(xino1->pub.name, "."); | |
xino1->pub.generation = xfs->generation; | |
xino1->pub.device_id = 0; | |
/* | |
* FUSE_ROOT_ID has no parent rather than being a parent | |
* of itself. This is intentional */ | |
xino1->parent = NULL; | |
xino1->next = NULL; | |
xino1->previous = NULL; | |
xino1->dir.begin = NULL; | |
xino1->dir.end = NULL; | |
xino2->pub.inum = DELETE_PENDING_ID; | |
xino2->pub.mode = (S_IFDIR | 0777) & ~umask; | |
xino2->pub.uid = uid; | |
xino2->pub.gid = gid; | |
xino2->pub.size = 0; | |
xino2->pub.atime = time(0); | |
xino2->pub.mtime = xino2->pub.atime; | |
xino2->pub.ctime = xino2->pub.atime; | |
strcpy(xino2->pub.name, ".delete-pending"); | |
xino2->pub.generation = xfs->generation; | |
xino2->pub.device_id = 0; | |
xino2->parent = NULL; | |
xino2->next = NULL; | |
xino2->previous = NULL; | |
xino2->dir.begin = NULL; | |
xino2->dir.end = NULL; | |
/* | |
* Uncomment this line to make the .delete-pending | |
* directory visible to the user in the root | |
*/ | |
/* link_inode_into_directory_node(xino1, xino2); */ | |
} | |
} | |
return xfs; | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_delete_xfs_fs(struct xfs_fs *xfs) | |
{ | |
if (xfs != NULL) | |
{ | |
if (xfs->inode_table != NULL) | |
{ | |
size_t i; | |
for (i = 0 ; i < xfs->inode_count; ++i) | |
{ | |
free(xfs->jnode_table[i]); | |
} | |
free(xfs->inode_table); | |
} | |
free(xfs->free_list); | |
free(xfs); | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
XFS_INODE * | |
xfs_add_entry(struct xfs_fs *xfs, fuse_ino_t parent_inum, | |
const char *name, mode_t mode) | |
{ | |
XFS_INODE *result = NULL; | |
XFS_INODE_ALL *parent = NULL; | |
/* Checks:- | |
* 1) the parent exists (and is a directory) | |
* 2) the caller is not inserting into the .delete-pending directory, | |
* 3) Name's not too long | |
* 4) Entry does not already exist | |
*/ | |
if (parent_inum < xfs->inode_count && | |
((parent = xfs->inode_table[parent_inum]) != NULL) && | |
(parent->pub.mode & S_IFDIR) != 0 && | |
parent_inum != DELETE_PENDING_ID && | |
strlen(name) <= XFS_MAXFILENAMELEN && | |
!xfs_lookup_in_dir(xfs, parent_inum, name)) | |
{ | |
/* Sanitise the mode so one-and-only-one of S_IFDIR and | |
* S_IFREG is set */ | |
if ((mode & S_IFDIR) != 0) | |
{ | |
mode = (mode & 0777) | S_IFDIR; | |
} | |
else | |
{ | |
mode = (mode & 0777) | S_IFREG; | |
} | |
/* Space for a new entry? */ | |
if (xfs->free_count > 0 || | |
grow_xfs(xfs, INODE_TABLE_ALLOCATION_GRANULARITY)) | |
{ | |
XFS_INODE_ALL *xino = NULL; | |
if ((xino = g_new0(XFS_INODE_ALL, 1)) != NULL) | |
{ | |
fuse_ino_t inum = xfs->free_list[--xfs->free_count]; | |
if (xfs->inode_table[inum] != NULL) | |
{ | |
log_error("Unexpected non-NULL value in inode table " | |
"entry %ld", inum); | |
} | |
xfs->inode_table[inum] = xino; | |
xino->pub.inum = inum; | |
xino->pub.mode = mode; | |
xino->pub.uid = parent->pub.uid; | |
xino->pub.gid = parent->pub.gid; | |
if (mode & S_IFDIR) | |
{ | |
xino->pub.size = 4096; | |
} | |
else | |
{ | |
xino->pub.size = 0; | |
} | |
xino->pub.atime = time(0); | |
xino->pub.mtime = xino->pub.atime; | |
xino->pub.ctime = xino->pub.atime; | |
strcpy(xino->pub.name, name); | |
xino->pub.generation = xfs->generation; | |
xino->pub.device_id = parent->pub.device_id; | |
xino->pub.lindex = 0; | |
xino->parent = NULL; | |
xino->next = NULL; | |
xino->previous = NULL; | |
link_inode_into_directory_node(parent, xino); | |
result = &xino->pub; | |
} | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_remove_directory_contents(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
XFS_INODE_ALL *xino = NULL; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
((xino->pub.mode & S_IFDIR) != 0)) | |
{ | |
XFS_INODE_ALL *e; | |
while ((e = xino->dir.end) != NULL) | |
{ | |
xfs_remove_entry(xfs, e->pub.inum); | |
} | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_remove_entry(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
XFS_INODE_ALL *xino = NULL; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL)) | |
{ | |
if ((xino->pub.mode & S_IFDIR) != 0) | |
{ | |
xfs_remove_directory_contents(xfs, inum); | |
} | |
unlink_inode_from_parent(xino); | |
if ((xino->pub.mode & S_IFREG) != 0 && xino->open_count > 0) | |
{ | |
link_inode_into_directory_node( | |
xfs->inode_table[DELETE_PENDING_ID], xino); | |
} | |
else | |
{ | |
xfs->free_list[xfs->free_count++] = inum; | |
xfs->inode_table[inum] = NULL; | |
/* | |
* Bump the generation when we return an inum to the free list, | |
* so that the caller can distinguish re-uses of the same inum. | |
*/ | |
++xfs->generation; | |
free(xino); | |
} | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
XFS_INODE * | |
xfs_get(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
return (inum < xfs->inode_count) ? &xfs->inode_table[inum]->pub : NULL; | |
} | |
/* ------------------------------------------------------------------------ */ | |
char * | |
xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
char *result = NULL; | |
XFS_INODE_ALL *xino = NULL; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL)) | |
{ | |
if (xino->pub.inum == FUSE_ROOT_ID) | |
{ | |
return strdup("/"); | |
} | |
else | |
{ | |
/* | |
* Add up the lengths of all the names up to the root, | |
* allowing one extra char for a '/' prefix for each element | |
*/ | |
size_t len = 0; | |
XFS_INODE_ALL *p; | |
for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) | |
{ | |
len += strlen(p->pub.name); | |
++len; /* Allow for '/' prefix */ | |
} | |
result = (char *) malloc(len + 1); | |
if (result != NULL) | |
{ | |
/* Construct the path from the end */ | |
char *end = result + len; | |
*end = '\0'; | |
for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) | |
{ | |
len = strlen(p->pub.name); | |
end -= (len + 1); | |
*end = '/'; | |
memcpy(end + 1, p->pub.name, len); | |
} | |
} | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
XFS_INODE * | |
xfs_lookup_in_dir(struct xfs_fs *xfs, fuse_ino_t inum, const char *name) | |
{ | |
XFS_INODE_ALL *xino; | |
XFS_INODE *result = NULL; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
(xino->pub.mode & S_IFDIR) != 0) | |
{ | |
XFS_INODE_ALL *p; | |
for (p = xino->dir.begin ; p != NULL; p = p->next) | |
{ | |
if (strcmp(p->pub.name, name) == 0) | |
{ | |
result = &p->pub; | |
break; | |
} | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
int | |
xfs_is_dir_empty(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
XFS_INODE_ALL *xino = NULL; | |
int result = 0; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
(xino->pub.mode & S_IFDIR) != 0) | |
{ | |
result = (xino->dir.begin == NULL); | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
unsigned int | |
xfs_is_under(struct xfs_fs *xfs, fuse_ino_t dir, fuse_ino_t entry) | |
{ | |
unsigned int result = 0; | |
XFS_INODE_ALL *dxino = NULL; | |
XFS_INODE_ALL *exino = NULL; | |
if (dir < xfs->inode_count && | |
((dxino = xfs->inode_table[dir]) != NULL) && | |
(dxino->pub.mode & S_IFDIR) != 0 && | |
entry < xfs->inode_count && | |
((exino = xfs->inode_table[entry]) != NULL)) | |
{ | |
unsigned int count = 0; | |
while (exino != NULL && exino != dxino) | |
{ | |
++count; | |
exino = exino->parent; | |
} | |
if (exino != NULL) | |
{ | |
result = count; | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
struct xfs_dir_handle * | |
xfs_opendir(struct xfs_fs *xfs, fuse_ino_t dir) | |
{ | |
XFS_INODE_ALL *xino = NULL; | |
struct xfs_dir_handle *result = NULL; | |
if (dir < xfs->inode_count && | |
((xino = xfs->inode_table[dir]) != NULL) && | |
(xino->pub.mode & S_IFDIR) != 0) | |
{ | |
result = g_new0(struct xfs_dir_handle, 1); | |
if (result) | |
{ | |
result->inum = xino->pub.inum; | |
result->generation = xino->pub.generation; | |
} | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
XFS_INODE * | |
xfs_readdir(struct xfs_fs *xfs, struct xfs_dir_handle *handle, off_t *off) | |
{ | |
XFS_INODE_ALL *result = NULL; | |
XFS_INODE_ALL *dxino = NULL; | |
XFS_INODE_ALL *xino = NULL; | |
/* Check the direcory is still valid */ | |
if (handle->inum < xfs->inode_count && | |
((dxino = xfs->inode_table[handle->inum]) != NULL) && | |
(dxino->pub.mode & S_IFDIR) != 0 && | |
handle->generation == dxino->pub.generation) | |
{ | |
fuse_ino_t inum; | |
if (*off == (off_t) -1) | |
{ | |
/* We're at the end already */ | |
} | |
else if ((inum = *off) == 0) | |
{ | |
/* First call */ | |
result = dxino->dir.begin; | |
} | |
else if (inum < xfs->inode_count && | |
(xino = xfs->inode_table[inum]) != 0 && | |
xino->parent == dxino) | |
{ | |
/* The node we're pointing to is still valid */ | |
result = xino; | |
} | |
else | |
{ | |
/* | |
* The file we wanted has been pulled out from under us. | |
* We will look forward in the inode table to try to | |
* discover the next inode in the directory. Because | |
* files are stored in inode order, this guarantees | |
* we'll meet POSIX requirements. | |
*/ | |
for (inum = inum + 1 ; inum < xfs->inode_count ; ++inum) | |
{ | |
if ((xino = xfs->inode_table[inum]) != 0 && | |
xino->parent == dxino) | |
{ | |
result = xino; | |
break; | |
} | |
} | |
} | |
} | |
/* Update the offset */ | |
if (result == NULL || result->next == NULL) | |
{ | |
/* We're done */ | |
*off = (off_t) -1; | |
} | |
else | |
{ | |
*off = (off_t)result->next->pub.inum; | |
} | |
/* Caller only sees public interface to the result */ | |
return (result) ? &result->pub : NULL; | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_closedir(struct xfs_fs *xfs, struct xfs_dir_handle *handle) | |
{ | |
free(handle); | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_increment_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
XFS_INODE_ALL *xino; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
(xino->pub.mode & S_IFREG) != 0) | |
{ | |
++xino->open_count; | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_decrement_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
XFS_INODE_ALL *xino; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
(xino->pub.mode & S_IFREG) != 0) | |
{ | |
if (xino->open_count > 0) | |
{ | |
--xino->open_count; | |
} | |
if (xino->open_count == 0 && | |
xino->parent == xfs->inode_table[DELETE_PENDING_ID]) | |
{ | |
/* We can get rid of this one now */ | |
xfs_remove_entry(xfs, inum); | |
} | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
unsigned int | |
xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) | |
{ | |
unsigned int result = 0; | |
XFS_INODE_ALL *xino; | |
if (inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
(xino->pub.mode & S_IFREG) != 0) | |
{ | |
result = xino->open_count; | |
} | |
return result; | |
} | |
/* ------------------------------------------------------------------------ */ | |
void | |
xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id) | |
{ | |
fuse_ino_t inum; | |
XFS_INODE_ALL *xino; | |
if (device_id != 0) | |
{ | |
/* Using xfs_remove_entry() is convenient, but it recurses | |
* in to directories. To make sure all entries are removed, set the | |
* open_count of all affected files to 0 first | |
*/ | |
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) | |
{ | |
if ((xino = xfs->inode_table[inum]) != NULL && | |
xino->pub.device_id == device_id && | |
(xino->pub.mode & S_IFREG) != 0) | |
{ | |
xino->open_count = 0; | |
} | |
} | |
/* Now we can be sure everything will be deleted correctly */ | |
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) | |
{ | |
if ((xino = xfs->inode_table[inum]) != NULL && | |
xino->pub.device_id == device_id) | |
{ | |
xfs_remove_entry(xfs, xino->pub.inum); | |
} | |
} | |
} | |
} | |
/* ------------------------------------------------------------------------ */ | |
int | |
xfs_check_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, | |
fuse_ino_t new_parent_inum, const char *name) | |
{ | |
XFS_INODE_ALL *xino; | |
XFS_INODE_ALL *parent; | |
return | |
(strlen(name) <= XFS_MAXFILENAMELEN && | |
inum < xfs->inode_count && | |
((xino = xfs->inode_table[inum]) != NULL) && | |
new_parent_inum != DELETE_PENDING_ID && | |
new_parent_inum < xfs->inode_count && | |
((parent = xfs->inode_table[new_parent_inum]) != NULL) && | |
(parent->pub.mode & S_IFDIR) != 0 && | |
xfs_is_under(xfs, inum, new_parent_inum) == 0); | |
} | |
/* ------------------------------------------------------------------------ */ | |
int | |
xfs_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, | |
fuse_ino_t new_parent_inum, const char *name) | |
{ | |
int result = EINVAL; | |
XFS_INODE_ALL *xino; | |
XFS_INODE_ALL *parent; | |
XFS_INODE *dest; | |
if (xfs_check_move_entry(xfs, inum, new_parent_inum, name)) | |
{ | |
xino = xfs->inode_table[inum]; | |
parent = xfs->inode_table[new_parent_inum]; | |
if (xino->parent != parent) | |
{ | |
/* We're moving between directories */ | |
/* Does the target name already exist in the destination? */ | |
if ((dest = xfs_lookup_in_dir(xfs, new_parent_inum, name)) != NULL) | |
{ | |
xfs_remove_entry(xfs, dest->inum); | |
} | |
unlink_inode_from_parent(xino); | |
link_inode_into_directory_node(parent, xino); | |
strcpy(xino->pub.name, name); | |
} | |
else if (strcmp(xino->pub.name, name) != 0) | |
{ | |
/* Same directory, but name has changed */ | |
if ((dest = xfs_lookup_in_dir(xfs, new_parent_inum, name)) != NULL) | |
{ | |
xfs_remove_entry(xfs, dest->inum); | |
} | |
strcpy(xino->pub.name, name); | |
} | |
result = 0; | |
} | |
return result; | |
} | |
#endif /* XRDP_FUSE */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment