#include "webdavd.h"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <paths.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include "webdav_cache.h"
#include "webdav_network.h"
#include "OpaqueIDs.h"
#include "LogMessage.h"
#define WEBDAV_STATFS_TIMEOUT 60
static time_t statfs_cache_time;
static struct statfs statfs_cache_buffer;
static int vfc_typenum;
static pthread_mutex_t webdav_cachefile_lock;
static int webdav_cachefile;
static int get_cachefile(int *fd);
static void save_cachefile(int fd);
static int associate_cachefile(int ref, int fd);
#define TMP_CACHE_DIR _PATH_TMP ".webdavcache"
#define CACHEFILE_TEMPLATE "webdav.XXXXXX"
static int get_cachefile(int *fd)
{
int error, mutexerror;
char pathbuf[MAXPATHLEN];
int retrycount;
error = 0;
error = pthread_mutex_lock(&webdav_cachefile_lock);
require_noerr_action(error, pthread_mutex_lock, webdav_kill(-1));
if ( webdav_cachefile < 0 )
{
retrycount = 0;
while ( retrycount < 5 )
{
++retrycount;
error = 0;
if ( *gWebdavCachePath == '\0' )
{
sprintf(gWebdavCachePath, "%s.%lu.XXXXXX", TMP_CACHE_DIR, (unsigned long)getpid());
require_action(mkdtemp(gWebdavCachePath) != NULL, mkdtemp, error = errno);
}
sprintf(pathbuf, "%s/%s", gWebdavCachePath, CACHEFILE_TEMPLATE);
*fd = mkstemp(pathbuf);
if ( *fd != -1 )
{
verify_noerr(unlink(pathbuf));
break;
}
else
{
error = errno;
if ( ENOENT == error )
{
*gWebdavCachePath = '\0';
continue;
}
else
{
debug_string("mkstemp failed");
break;
}
}
}
}
else
{
*fd = webdav_cachefile;
webdav_cachefile = -1;
}
mkdtemp:
mutexerror = pthread_mutex_unlock(&webdav_cachefile_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return ( error );
}
static void save_cachefile(int fd)
{
int mutexerror;
mutexerror = pthread_mutex_lock(&webdav_cachefile_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1));
if ( webdav_cachefile < 0 )
{
webdav_cachefile = fd;
}
else
{
close(fd);
}
mutexerror = pthread_mutex_unlock(&webdav_cachefile_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return;
}
static int associate_cachefile(int ref, int fd)
{
int error = 0;
int mib[5];
mib[0] = CTL_VFS;
mib[1] = vfc_typenum;
mib[2] = WEBDAV_ASSOCIATECACHEFILE_SYSCTL;
mib[3] = ref;
mib[4] = fd;
require_noerr_action(sysctl(mib, 5, NULL, NULL, NULL, 0), sysctl, error = errno);
sysctl:
return ( error );
}
int filesystem_init(int typenum)
{
pthread_mutexattr_t mutexattr;
int error;
vfc_typenum = typenum;
bzero(&statfs_cache_buffer, sizeof(statfs_cache_buffer));
statfs_cache_time = 0;
webdav_cachefile = -1;
error = pthread_mutexattr_init(&mutexattr);
require_noerr(error, pthread_mutexattr_init);
error = pthread_mutex_init(&webdav_cachefile_lock, &mutexattr);
require_noerr(error, pthread_mutex_init);
pthread_mutex_init:
pthread_mutexattr_init:
return ( error );
}
int filesystem_open(struct webdav_request_open *request_open,
struct webdav_reply_open *reply_open)
{
int error;
struct node_entry *node;
int theCacheFile;
char *locktoken;
reply_open->pid = 0;
locktoken = NULL;
error = RetrieveDataFromOpaqueID(request_open->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
theCacheFile = -1;
error = get_cachefile(&theCacheFile);
require_noerr_quiet(error, get_cachefile);
if (node->node_type == WEBDAV_FILE_TYPE)
{
int write_mode;
if ( NODE_FILE_IS_CACHED(node) )
{
save_cachefile(theCacheFile);
node->file_inactive_time = 0;
}
else
{
error = nodecache_add_file_cache(node, theCacheFile);
require_noerr_action_quiet(error, nodecache_add_file_cache, save_cachefile(theCacheFile));
}
write_mode = ((request_open->flags & O_ACCMODE) != O_RDONLY);
if ( write_mode )
{
error = network_lock(request_open->pcr.pcr_uid, FALSE, node);
if ( error == ENOENT )
{
(void) nodecache_delete_node(node, TRUE);
error = ESTALE;
goto bad_obj_id;
}
}
if ( !error )
{
if ( write_mode && (request_open->flags & O_TRUNC) )
{
require_noerr_action(fchflags(node->file_fd, 0), fchflags, error = errno);
require_noerr_action(ftruncate(node->file_fd, 0LL), ftruncate, error = errno);
node->file_status = WEBDAV_DOWNLOAD_FINISHED;
}
else
{
error = network_open(request_open->pcr.pcr_uid, node, write_mode);
if ( error == ENOENT )
{
(void) nodecache_delete_node(node, TRUE);
error = ESTALE;
goto bad_obj_id;
}
}
}
}
else
{
error = nodecache_add_file_cache(node, theCacheFile);
require_noerr_action_quiet(error, nodecache_add_file_cache, save_cachefile(theCacheFile));
node->file_status = WEBDAV_DOWNLOAD_FINISHED;
}
if ( !error )
{
error = associate_cachefile(request_open->ref, node->file_fd);
if ( 0 == error )
{
reply_open->pid = getpid();
}
else
{
error = errno;
}
}
fchflags:
ftruncate:
if ( error )
{
if ( node->file_locktoken != NULL )
{
(void) network_unlock(node);
}
nodecache_remove_file_cache(node);
}
nodecache_add_file_cache:
get_cachefile:
deleted_node:
bad_obj_id:
return ( error );
}
int filesystem_close(struct webdav_request_close *request_close)
{
int error = 0;
struct node_entry *node;
error = RetrieveDataFromOpaqueID(request_close->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action(NODE_FILE_IS_CACHED(node), not_open, error = EBADF);
if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_IN_PROGRESS )
{
node->file_status |= WEBDAV_DOWNLOAD_TERMINATED;
}
while ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_IN_PROGRESS )
{
usleep(10000);
}
time(&node->file_inactive_time);
if ( node->put_ctx != NULL ) {
error = cleanup_seq_write(node->put_ctx);
free (node->put_ctx);
node->put_ctx = NULL;
}
if ( node->file_locktoken )
{
if ( !NODE_IS_DELETED(node) )
{
error = network_unlock(node);
if ( error == ENOENT )
{
(void) nodecache_delete_node(node, TRUE);
error = ESTALE;
goto bad_obj_id;
}
}
}
if ( error || NODE_IS_DELETED(node) || (node->node_type == WEBDAV_DIR_TYPE) )
{
(void)nodecache_remove_file_cache(node);
}
not_open:
bad_obj_id:
return (error);
}
int filesystem_lookup(struct webdav_request_lookup *request_lookup, struct webdav_reply_lookup *reply_lookup)
{
int error;
struct node_entry *node;
struct node_entry *parent_node;
struct stat statbuf;
int lookup;
node = NULL;
error = RetrieveDataFromOpaqueID(request_lookup->dir_id, (void **)&parent_node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
if ( request_lookup->force_lookup )
{
lookup = TRUE;
}
else
{
error = nodecache_get_node(parent_node, request_lookup->name_length, request_lookup->name, FALSE, FALSE, 0, &node);
if ( error )
{
lookup = TRUE;
}
else
{
lookup = !node_attributes_valid(node, request_lookup->pcr.pcr_uid);
}
}
if ( lookup )
{
error = network_lookup(request_lookup->pcr.pcr_uid, parent_node,
request_lookup->name, request_lookup->name_length, &statbuf);
if ( !error )
{
error = nodecache_get_node(parent_node, request_lookup->name_length, request_lookup->name, TRUE, FALSE,
S_ISREG(statbuf.st_mode) ? WEBDAV_FILE_TYPE : WEBDAV_DIR_TYPE, &node);
if ( !error )
{
statbuf.st_ino = node->fileid;
error = nodecache_add_attributes(node, request_lookup->pcr.pcr_uid, &statbuf, NULL);
}
}
else if ( (error == ENOENT) && (node != NULL) )
{
(void) nodecache_delete_node(node, TRUE);
node = NULL;
}
}
else if ( node == NULL )
{
error = ENOENT;
}
if ( !error )
{
reply_lookup->obj_id = node->nodeid;
reply_lookup->obj_fileid = node->fileid;
reply_lookup->obj_type = node->node_type;
reply_lookup->obj_atime = node->attr_stat.st_atimespec;
reply_lookup->obj_mtime = node->attr_stat.st_mtimespec;
reply_lookup->obj_ctime = node->attr_stat.st_ctimespec;
reply_lookup->obj_filesize = node->attr_stat.st_size;
}
bad_obj_id:
return (error);
}
int filesystem_getattr(struct webdav_request_getattr *request_getattr, struct webdav_reply_getattr *reply_getattr)
{
int error;
struct node_entry *node;
struct stat statbuf;
error = RetrieveDataFromOpaqueID(request_getattr->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
if ( !node_attributes_valid(node, request_getattr->pcr.pcr_uid) )
{
error = network_getattr( request_getattr->pcr.pcr_uid, node, &statbuf);
if ( !error )
{
error = nodecache_add_attributes(node, request_getattr->pcr.pcr_uid, &statbuf, NULL);
}
}
else
{
error = 0;
}
if ( !error )
{
bcopy(&node->attr_stat, &reply_getattr->obj_attr, sizeof(struct stat));
}
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_statfs(struct webdav_request_statfs *request_statfs,
struct webdav_reply_statfs *reply_statfs)
{
int error;
time_t thetime;
int call_server;
struct node_entry *node;
error = RetrieveDataFromOpaqueID(request_statfs->root_obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
if ( gSuppressAllUI )
{
error = 0;
}
else
{
thetime = time(0);
if ( thetime != -1 )
{
call_server = (statfs_cache_time == 0) || (thetime > (statfs_cache_time + WEBDAV_STATFS_TIMEOUT));
}
else
{
thetime = 0;
call_server = TRUE;
}
if ( call_server )
{
error = network_statfs(request_statfs->pcr.pcr_uid, node, &statfs_cache_buffer);
if ( !error )
{
statfs_cache_time = thetime;
}
}
else
{
error = 0;
}
if ( !error )
{
bcopy(&statfs_cache_buffer, &reply_statfs->fs_attr, sizeof(struct statfs));
}
}
bad_obj_id:
return (error);
}
int filesystem_mount(int *a_mount_args)
{
int error;
error = network_mount(getuid(), a_mount_args);
return (error);
}
int filesystem_create(struct webdav_request_create *request_create, struct webdav_reply_create *reply_create)
{
int error;
struct node_entry *node;
struct node_entry *parent_node;
time_t creation_date;
int theCacheFile;
error = RetrieveDataFromOpaqueID(request_create->dir_id, (void **)&parent_node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
error = network_create(request_create->pcr.pcr_uid, parent_node, request_create->name, request_create->name_length, &creation_date);
if ( !error )
{
if ( (creation_date != -1) &&
(parent_node->attr_stat.st_mtimespec.tv_sec <= creation_date) &&
node_attributes_valid(parent_node, request_create->pcr.pcr_uid) )
{
parent_node->attr_stat.st_mtimespec.tv_sec = creation_date;
parent_node->attr_stat.st_atimespec = parent_node->attr_stat.st_ctimespec = parent_node->attr_stat.st_mtimespec;
parent_node->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(parent_node);
}
error = nodecache_get_node(parent_node, request_create->name_length, request_create->name, TRUE, TRUE, WEBDAV_FILE_TYPE, &node);
if ( !error )
{
if ( creation_date != -1 )
{
struct stat statbuf;
bzero((void *)&statbuf, sizeof(struct stat));
statbuf.st_dev = 0;
statbuf.st_ino = node->fileid;
statbuf.st_mode = S_IFREG | S_IRWXU;
statbuf.st_nlink = 1;
statbuf.st_uid = UNKNOWNUID;
statbuf.st_gid = UNKNOWNUID;
statbuf.st_rdev = 0;
statbuf.st_mtimespec.tv_sec = creation_date;
statbuf.st_atimespec = statbuf.st_ctimespec = statbuf.st_mtimespec;
statbuf.st_size = 0;
statbuf.st_blocks = 0;
statbuf.st_blksize = WEBDAV_IOSIZE;
statbuf.st_flags = 0;
statbuf.st_gen = 0;
error = nodecache_add_attributes(node, request_create->pcr.pcr_uid, &statbuf, NULL);
}
theCacheFile = -1;
error = get_cachefile(&theCacheFile);
if ( error == 0 )
{
error = nodecache_add_file_cache(node, theCacheFile);
time(&node->file_validated_time);
node->file_status = WEBDAV_DOWNLOAD_FINISHED;
time(&node->file_inactive_time);
}
reply_create->obj_id = node->nodeid;
reply_create->obj_fileid = node->fileid;
}
}
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_mkdir(struct webdav_request_mkdir *request_mkdir, struct webdav_reply_mkdir *reply_mkdir)
{
int error;
struct node_entry *node;
struct node_entry *parent_node;
time_t creation_date;
error = RetrieveDataFromOpaqueID(request_mkdir->dir_id, (void **)&parent_node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
error = network_mkdir(request_mkdir->pcr.pcr_uid, parent_node, request_mkdir->name, request_mkdir->name_length, &creation_date);
if ( !error )
{
if ( (creation_date != -1) &&
(parent_node->attr_stat.st_mtimespec.tv_sec <= creation_date) &&
node_attributes_valid(parent_node, request_mkdir->pcr.pcr_uid) )
{
parent_node->attr_stat.st_mtimespec.tv_sec = creation_date;
parent_node->attr_stat.st_atimespec = parent_node->attr_stat.st_ctimespec = parent_node->attr_stat.st_mtimespec;
parent_node->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(parent_node);
}
error = nodecache_get_node(parent_node, request_mkdir->name_length, request_mkdir->name, TRUE, TRUE, WEBDAV_DIR_TYPE, &node);
if ( !error )
{
if ( creation_date != -1 )
{
struct stat statbuf;
bzero((void *)&statbuf, sizeof(struct stat));
statbuf.st_dev = 0;
statbuf.st_ino = node->fileid;
statbuf.st_mode = S_IFDIR | S_IRWXU;
statbuf.st_nlink = 1;
statbuf.st_uid = UNKNOWNUID;
statbuf.st_gid = UNKNOWNUID;
statbuf.st_rdev = 0;
statbuf.st_mtimespec.tv_sec = creation_date;
statbuf.st_atimespec = statbuf.st_ctimespec = statbuf.st_mtimespec;
statbuf.st_size = WEBDAV_DIR_SIZE;
statbuf.st_blocks = ((statbuf.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
statbuf.st_blksize = WEBDAV_IOSIZE;
statbuf.st_flags = 0;
statbuf.st_gen = 0;
error = nodecache_add_attributes(node, request_mkdir->pcr.pcr_uid, &statbuf, NULL);
}
reply_mkdir->obj_id = node->nodeid;
reply_mkdir->obj_fileid = node->fileid;
}
}
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_read(struct webdav_request_read *request_read, char **a_byte_addr, size_t *a_size)
{
int error;
struct node_entry *node;
error = RetrieveDataFromOpaqueID(request_read->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
error = network_read(request_read->pcr.pcr_uid, node,
request_read->offset, request_read->count, a_byte_addr, a_size);
deleted_node:
bad_obj_id:
return ( error );
}
int filesystem_rename(struct webdav_request_rename *request_rename)
{
int error = 0;
struct node_entry *f_node;
struct node_entry *t_node;
struct node_entry *parent_node;
time_t rename_date;
error = RetrieveDataFromOpaqueID(request_rename->from_obj_id, (void **)&f_node);
require_noerr_action_quiet(error, bad_from_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(f_node), deleted_node, error = ESTALE);
error = RetrieveDataFromOpaqueID(request_rename->to_dir_id, (void **)&parent_node);
require_noerr_action_quiet(error, bad_to_dir_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
if ( request_rename->to_obj_id != kInvalidOpaqueID )
{
error = RetrieveDataFromOpaqueID(request_rename->to_obj_id, (void **)&t_node);
require_noerr_action_quiet(error, bad_to_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(t_node), deleted_node, error = ESTALE);
if ( f_node->node_type != t_node->node_type )
{
error = (f_node->node_type == WEBDAV_FILE_TYPE) ? EISDIR : ENOTDIR;
}
else
{
error = 0;
}
}
else
{
t_node = NULL;
error = 0;
}
if ( !error )
{
error = network_rename(request_rename->pcr.pcr_uid, f_node, t_node,
parent_node, request_rename->to_name, request_rename->to_name_length, &rename_date);
if ( !error )
{
if ( (rename_date != -1) &&
(f_node->parent->attr_stat.st_mtimespec.tv_sec <= rename_date) &&
node_attributes_valid(f_node->parent, request_rename->pcr.pcr_uid) )
{
f_node->parent->attr_stat.st_mtimespec.tv_sec = rename_date;
f_node->parent->attr_stat.st_atimespec = f_node->parent->attr_stat.st_ctimespec = f_node->parent->attr_stat.st_mtimespec;
f_node->parent->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(f_node->parent);
}
if ( f_node->parent != parent_node )
{
if ( (rename_date != -1) &&
(parent_node->attr_stat.st_mtimespec.tv_sec <= rename_date) &&
node_attributes_valid(parent_node, request_rename->pcr.pcr_uid) )
{
parent_node->attr_stat.st_mtimespec.tv_sec = rename_date;
parent_node->attr_stat.st_atimespec = parent_node->attr_stat.st_ctimespec = parent_node->attr_stat.st_mtimespec;
parent_node->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(parent_node);
}
}
if ( t_node != NULL )
{
if ( nodecache_delete_node(t_node, FALSE) != 0 )
{
debug_string("nodecache_delete_node failed");
}
}
if ( nodecache_move_node(f_node, parent_node, request_rename->to_name_length, request_rename->to_name) != 0 )
{
debug_string("nodecache_move_node failed");
}
statfs_cache_time = 0;
}
}
deleted_node:
bad_to_obj_id:
bad_to_dir_id:
bad_from_obj_id:
return (error);
}
int filesystem_remove(struct webdav_request_remove *request_remove)
{
int error;
struct node_entry *node;
time_t remove_date;
error = RetrieveDataFromOpaqueID(request_remove->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
error = network_remove(request_remove->pcr.pcr_uid, node, &remove_date);
if ( (!error) || (error == ENOENT) )
{
if ( (remove_date != -1) &&
(node->parent->attr_stat.st_mtimespec.tv_sec <= remove_date) &&
node_attributes_valid(node->parent, request_remove->pcr.pcr_uid) )
{
node->parent->attr_stat.st_mtimespec.tv_sec = remove_date;
node->parent->attr_stat.st_atimespec = node->parent->attr_stat.st_ctimespec = node->parent->attr_stat.st_mtimespec;
node->parent->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(node->parent);
}
if ( nodecache_delete_node(node, FALSE) != 0 )
{
debug_string("nodecache_delete_node failed");
}
statfs_cache_time = 0;
}
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_rmdir(struct webdav_request_rmdir *request_rmdir)
{
int error;
struct node_entry *node;
time_t remove_date;
error = RetrieveDataFromOpaqueID(request_rmdir->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
error = network_rmdir(request_rmdir->pcr.pcr_uid, node, &remove_date);
if ( !error )
{
if ( (remove_date != -1) &&
(node->parent->attr_stat.st_mtimespec.tv_sec <= remove_date) &&
node_attributes_valid(node->parent, request_rmdir->pcr.pcr_uid) )
{
node->parent->attr_stat.st_mtimespec.tv_sec = remove_date;
node->parent->attr_stat.st_atimespec = node->parent->attr_stat.st_ctimespec = node->parent->attr_stat.st_mtimespec;
node->parent->attr_time = time(NULL);
}
else
{
(void)nodecache_remove_attributes(node->parent);
}
if ( nodecache_delete_node(node, TRUE) != 0 )
{
debug_string("nodecache_delete_node failed");
}
statfs_cache_time = 0;
}
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_fsync(struct webdav_request_fsync *request_fsync)
{
int error;
struct node_entry *node;
off_t file_length;
time_t file_last_modified;
error = RetrieveDataFromOpaqueID(request_fsync->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
require_action(NODE_FILE_IS_CACHED(node), not_open, error = EBADF);
require_action((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED, still_downloading, error = EIO);
error = network_fsync(request_fsync->pcr.pcr_uid, node, &file_length, &file_last_modified);
if ( (file_length == -1) || (file_last_modified == -1) )
{
(void)nodecache_remove_attributes(node);
}
else
{
struct stat statbuf;
bzero((void *)&statbuf, sizeof(struct stat));
statbuf.st_dev = 0;
statbuf.st_ino = node->fileid;
statbuf.st_mode = S_IFREG | S_IRWXU;
statbuf.st_nlink = 1;
statbuf.st_uid = UNKNOWNUID;
statbuf.st_gid = UNKNOWNUID;
statbuf.st_rdev = 0;
statbuf.st_mtimespec.tv_sec = file_last_modified;
statbuf.st_atimespec = statbuf.st_ctimespec = statbuf.st_mtimespec;
statbuf.st_size = file_length;
statbuf.st_blocks = ((statbuf.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
statbuf.st_blksize = WEBDAV_IOSIZE;
statbuf.st_flags = 0;
statbuf.st_gen = 0;
error = nodecache_add_attributes(node, request_fsync->pcr.pcr_uid, &statbuf, NULL);
}
statfs_cache_time = 0;
still_downloading:
not_open:
deleted_node:
bad_obj_id:
return ( error );
}
int filesystem_write_seq(struct webdav_request_writeseq *request_sq_wr)
{
int error;
ssize_t bytesRead;
size_t totalBytesRead;
int nbytes;
struct node_entry *node;
struct stream_put_ctx *ctx = NULL;
struct seqwrite_mgr_req mgr_req;
struct timespec timeout;
pthread_mutexattr_t mutexattr;
error = 0;
error = RetrieveDataFromOpaqueID(request_sq_wr->obj_id, (void **)&node);
require_noerr_action_quiet(error, out1, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), out1, error = ESTALE);
require_action(NODE_FILE_IS_CACHED(node), out1, error = EBADF);
if ( request_sq_wr->offset == 0 ) {
error = setup_seq_write(request_sq_wr->pcr.pcr_uid, node, request_sq_wr->file_len);
}
if (error) {
syslog(LOG_ERR, "%s: setup_seq_write returned %d\n", __FUNCTION__, error);
goto out1;
}
ctx = node->put_ctx;
if (request_sq_wr->is_retry)
ctx->is_retry = 1;
pthread_mutex_lock(&ctx->ctx_lock);
mgr_req.req = request_sq_wr;
mgr_req.is_retry = request_sq_wr->is_retry;
mgr_req.data = (unsigned char *)malloc(BODY_BUFFER_SIZE);
if ( mgr_req.data == NULL ) {
syslog(LOG_ERR, "%s: malloc of data buffer failed", __FUNCTION__);
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
error = pthread_mutexattr_init(&mutexattr);
if (error) {
syslog(LOG_ERR, "%s: init ctx mutexattr failed, error %d", __FUNCTION__, error);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
error = pthread_mutex_init(&mgr_req.req_lock, &mutexattr);
if (error) {
syslog(LOG_ERR, "%s: init ctx_lock failed, error %d", __FUNCTION__, error);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
error = pthread_cond_init(&mgr_req.req_condvar, NULL);
if (error) {
syslog(LOG_ERR, "%s: init ctx_condvar failed, error %d", __FUNCTION__, error);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
if (node->file_fd == -1)
{
syslog(LOG_ERR, "%s: cache file descriptor is -1, failed.", __FUNCTION__ );
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
if ( lseek(node->file_fd, request_sq_wr->offset, SEEK_SET) < 0) {
syslog(LOG_ERR, "%s: lseek errno %d, failed.", __FUNCTION__, errno);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
pthread_mutex_unlock(&ctx->ctx_lock);
totalBytesRead = 0, bytesRead = 0;
while( 1 ) {
pthread_mutex_lock(&ctx->ctx_lock);
if ( ctx->mgr_status == WR_MGR_DONE || ctx->finalStatusValid == true ) {
error = ctx->finalStatus;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
if ( totalBytesRead >= request_sq_wr->count ) {
pthread_mutex_unlock(&ctx->ctx_lock);
break;
}
nbytes = MIN( request_sq_wr->count - totalBytesRead, BODY_BUFFER_SIZE );
bytesRead = read( node->file_fd, mgr_req.data, nbytes );
if ( bytesRead < 0 ) {
syslog(LOG_ERR, "%s: read() cache file returned error %d, failed.", __FUNCTION__, errno);
error = errno;
ctx->finalStatus = error;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
mgr_req.type = SEQWRITE_CHUNK;
mgr_req.request_done = false;
mgr_req.chunkLen = bytesRead;
mgr_req.chunkWritten = false;
mgr_req.error = 0;
if (queue_writemgr_request_locked(ctx, &mgr_req) < 0) {
syslog(LOG_ERR, "%s: queue_writemgr_request_locked failed.", __FUNCTION__);
error = EIO;
ctx->finalStatus = error;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
pthread_mutex_unlock(&ctx->ctx_lock);
timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_REQUEST_TIMEOUT;
timeout.tv_nsec = 0;
pthread_mutex_lock(&mgr_req.req_lock);
while (mgr_req.request_done == false && ctx->finalStatusValid == false) {
error = pthread_cond_timedwait(&mgr_req.req_condvar, &mgr_req.req_lock, &timeout);
if ( error != 0 ) {
syslog(LOG_ERR, "%s: pthread_cond_timedwait returned error %d, failed.", __FUNCTION__, error);
pthread_mutex_lock(&ctx->ctx_lock);
if ( error == ETIMEDOUT ) {
ctx->finalStatus = ETIMEDOUT;
ctx->finalStatusValid = true;
} else {
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
error = EIO;
}
pthread_mutex_unlock(&ctx->ctx_lock);
pthread_mutex_unlock(&mgr_req.req_lock);
goto out1;
}
}
pthread_mutex_unlock(&mgr_req.req_lock);
totalBytesRead += bytesRead;
}
if (mgr_req.request_done == true) {
error = mgr_req.error;
} else {
error = ctx->finalStatus;
}
out1:
if (error) {
cleanup_seq_write(ctx);
} else {
ctx->is_retry = 0;
}
if (mgr_req.data != NULL) free(mgr_req.data);
return ( error );
}
int filesystem_readdir(struct webdav_request_readdir *request_readdir)
{
int error;
struct node_entry *node;
error = RetrieveDataFromOpaqueID(request_readdir->obj_id, (void **)&node);
require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
error = network_readdir(request_readdir->pcr.pcr_uid, request_readdir->cache, node);
deleted_node:
bad_obj_id:
return (error);
}
int filesystem_lock(struct node_entry *node)
{
int error;
require_action(node != NULL, null_node, error = EIO);
if ( node->file_locktoken != NULL )
{
error = network_lock(0, TRUE, node);
}
else
{
error = 0;
}
null_node:
return ( error );
}
int filesystem_invalidate_caches(struct webdav_request_invalcaches *request_invalcaches)
{
int error;
require_action(request_invalcaches->pcr.pcr_uid == gProcessUID, not_permitted, error = EPERM);
nodecache_invalidate_caches();
error = 0;
not_permitted:
return (error);
}