1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "webdavd.h"
25
26#include <stdio.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <string.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <pthread.h>
33#include <paths.h>
34#include <sys/types.h>
35#include <sys/param.h>
36#include <sys/stat.h>
37#include <sys/sysctl.h>
38
39#include "webdav_cache.h"
40#include "webdav_network.h"
41#include "OpaqueIDs.h"
42#include "LogMessage.h"
43
44/*****************************************************************************/
45// The maximum size of an upload or download to allow the
46// system to cache.
47extern uint64_t webdavCacheMaximumSize;
48
49/*****************************************************************************/
50
51#define WEBDAV_STATFS_TIMEOUT 60	/* Number of seconds statfs_cache_buffer is valid */
52static time_t statfs_cache_time;
53static struct statfs statfs_cache_buffer;
54
55int g_vfc_typenum;
56
57static pthread_mutex_t webdav_cachefile_lock;	/* this mutex protects webdav_cachefile */
58static int webdav_cachefile;	/* file descriptor for an empty, unlinked cache file or -1 */
59
60/*****************************************************************************/
61
62static int get_cachefile(int *fd);
63static void save_cachefile(int fd);
64static int associate_cachefile(int ref, int fd);
65
66/*****************************************************************************/
67
68#define TMP_CACHE_DIR _PATH_TMP ".webdavcache"		/* Directory for local file cache */
69#define CACHEFILE_TEMPLATE "webdav.XXXXXX"			/* template for cache files */
70
71/* get_cachefile returns the fd for a cache file. If webdav_cachefile is not
72 * storing a cache file fd, open/create a new temp file and return it.
73 * Otherwise, return the stored cache file fd.
74 */
75static int get_cachefile(int *fd)
76{
77	int error, mutexerror;
78	char pathbuf[MAXPATHLEN];
79	int retrycount;
80
81	error = 0;
82
83	error = pthread_mutex_lock(&webdav_cachefile_lock);
84	require_noerr_action(error, pthread_mutex_lock, webdav_kill(-1));
85
86	/* did a previous call leave a cache file for us to use? */
87	if ( webdav_cachefile < 0 )
88	{
89		/* no, so create a temp file */
90		retrycount = 0;
91		/* don't get stuck here forever */
92		while ( retrycount < 5 )
93		{
94			++retrycount;
95			error = 0;
96			if ( *gWebdavCachePath == '\0' )
97			{
98				/* create a template with our pid */
99				snprintf(gWebdavCachePath, MAXPATHLEN + 1, "%s.%lu.XXXXXX", TMP_CACHE_DIR, (unsigned long)getpid());
100
101				/* create the cache directory */
102				require_action(mkdtemp(gWebdavCachePath) != NULL, mkdtemp, error = errno);
103			}
104
105			/* create a template for the cache file */
106			snprintf(pathbuf, MAXPATHLEN, "%s/%s", gWebdavCachePath, CACHEFILE_TEMPLATE);
107
108			/* create and open the cache file */
109			*fd = mkstemp(pathbuf);
110			if ( *fd != -1 )
111			{
112				/* unlink it so the last close will delete it */
113				verify_noerr(unlink(pathbuf));
114				break;	/* break with success */
115			}
116			else
117			{
118				error = errno;
119				if ( ENOENT == error )
120				{
121					/* the gWebdavCachePath directory is missing, clear the old one and try again */
122					*gWebdavCachePath = '\0';
123					continue;
124				}
125				else
126				{
127					debug_string("mkstemp failed");
128					break;	/* break with error */
129				}
130			}
131		}
132	}
133	else
134	{
135		/* yes, so grab it */
136		*fd = webdav_cachefile;
137		webdav_cachefile = -1;
138	}
139
140mkdtemp:
141
142	mutexerror = pthread_mutex_unlock(&webdav_cachefile_lock);
143	require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1));
144
145pthread_mutex_unlock:
146pthread_mutex_lock:
147
148	return ( error );
149}
150
151/*****************************************************************************/
152
153/* save_cachefile saves a cache file fd that wasn't needed. If there is already
154 * stored a cache file fd, then the input fd is closed (closing will only
155 * happen when there there is a race between multiple open requests so it
156 * should be rare).
157 */
158static void save_cachefile(int fd)
159{
160	int mutexerror;
161
162	mutexerror = pthread_mutex_lock(&webdav_cachefile_lock);
163	require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1));
164
165	/* are we already storing a cache file that wasn't used? */
166	if ( webdav_cachefile < 0 )
167	{
168		/* no, so store this one */
169		webdav_cachefile = fd;
170	}
171	else
172	{
173		/* yes, so close this one */
174		close(fd);
175	}
176
177	mutexerror = pthread_mutex_unlock(&webdav_cachefile_lock);
178	require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1));
179
180pthread_mutex_unlock:
181pthread_mutex_lock:
182
183	return;
184}
185
186/*****************************************************************************/
187
188static int associate_cachefile(int ref, int fd)
189{
190	int error = 0;
191	int mib[5];
192
193	/* setup mib for the request */
194	mib[0] = CTL_VFS;
195	mib[1] = g_vfc_typenum;
196	mib[2] = WEBDAV_ASSOCIATECACHEFILE_SYSCTL;
197	mib[3] = ref;
198	mib[4] = fd;
199
200	require_noerr_action(sysctl(mib, 5, NULL, NULL, NULL, 0), sysctl, error = errno);
201
202sysctl:
203
204	return ( error );
205}
206
207/*****************************************************************************/
208
209int filesystem_init(int typenum)
210{
211	pthread_mutexattr_t mutexattr;
212	int error;
213
214	g_vfc_typenum = typenum;
215
216	/* Set up the statfs timeout & buffer */
217	bzero(&statfs_cache_buffer, sizeof(statfs_cache_buffer));
218	statfs_cache_time = 0;
219
220	webdav_cachefile = -1;	/* closed */
221
222	/* set up the lock on the queues */
223	error = pthread_mutexattr_init(&mutexattr);
224	require_noerr(error, pthread_mutexattr_init);
225
226	error = pthread_mutex_init(&webdav_cachefile_lock, &mutexattr);
227	require_noerr(error, pthread_mutex_init);
228
229pthread_mutex_init:
230pthread_mutexattr_init:
231
232	return ( error );
233}
234
235/*****************************************************************************/
236
237int filesystem_open(struct webdav_request_open *request_open,
238		struct webdav_reply_open *reply_open)
239{
240	int error;
241	struct node_entry *node;
242	int theCacheFile;
243	char *locktoken;
244
245	reply_open->pid = 0;
246
247	locktoken = NULL;
248
249	error = RetrieveDataFromOpaqueID(request_open->obj_id, (void **)&node);
250	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
251
252	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
253
254	/* get a cache file */
255	theCacheFile = -1;
256	error = get_cachefile(&theCacheFile);
257	require_noerr_quiet(error, get_cachefile);
258
259	if (node->node_type == WEBDAV_FILE_TYPE)
260	{
261		int write_mode;
262
263		if ( NODE_FILE_IS_CACHED(node) )
264		{
265			/* save the cache file we didn't need */
266			save_cachefile(theCacheFile);
267
268			/* mark the old cache file active again */
269			node->file_inactive_time = 0;
270		}
271		else
272		{
273			error = nodecache_add_file_cache(node, theCacheFile);
274			require_noerr_action_quiet(error, nodecache_add_file_cache, save_cachefile(theCacheFile));
275
276			/* If we get an error beyond this point we need to call nodecache_remove_file_cache() */
277		}
278
279		write_mode = ((request_open->flags & O_ACCMODE) != O_RDONLY);
280
281		if ( write_mode )
282		{
283			/* If we are opening this file for write access, lock it first,
284			  before we copy it into the cache file from the server,
285			  or truncate the cache file. */
286			error = network_lock(request_open->pcr.pcr_uid, FALSE, node);
287			if ( error == ENOENT )
288			{
289				/* the server says it's gone so delete it and its descendants */
290				(void) nodecache_delete_node(node, TRUE);
291				error = ESTALE;
292				goto bad_obj_id;
293			}
294		}
295
296		if ( !error )
297		{
298			if ( write_mode && (request_open->flags & O_TRUNC) )
299			{
300				/*
301				 * If opened for write and truncate, we can set the length to zero
302				 * and not get it from the server.
303				 */
304				require_noerr_action(fchflags(node->file_fd, 0), fchflags, error = errno);
305				require_noerr_action(ftruncate(node->file_fd, 0LL), ftruncate, error = errno);
306				node->file_status = WEBDAV_DOWNLOAD_FINISHED;
307			}
308			else
309			{
310				error = network_open(request_open->pcr.pcr_uid, node, write_mode);
311				if ( error == ENOENT )
312				{
313					/* the server says it's gone so delete it and its descendants */
314					(void) nodecache_delete_node(node, TRUE);
315					error = ESTALE;
316					goto bad_obj_id;
317				}
318			}
319		}
320	}
321	else
322	{
323		/* it's a directory */
324
325		error = nodecache_add_file_cache(node, theCacheFile);
326		require_noerr_action_quiet(error, nodecache_add_file_cache, save_cachefile(theCacheFile));
327		/* If we get an error beyond this point we need to call nodecache_remove_file_cache() */
328
329		/* Directory opens are always done in the foreground so set the
330		 * download status to done
331		 */
332		node->file_status = WEBDAV_DOWNLOAD_FINISHED;
333	}
334
335	if ( !error )
336	{
337		/* all good so far -- associate the cachefile with the webdav file */
338		error = associate_cachefile(request_open->ref, node->file_fd);
339		if ( 0 == error )
340		{
341			reply_open->pid = getpid();
342		}
343		else
344		{
345			error = errno;
346		}
347	}
348
349fchflags:
350ftruncate:
351
352	/* clean up if error */
353	if ( error )
354	{
355		(void) network_unlock(node);
356
357		/* remove it from the file cache */
358		nodecache_remove_file_cache(node);
359	}
360
361nodecache_add_file_cache:
362get_cachefile:
363deleted_node:
364bad_obj_id:
365
366	return ( error );
367}
368
369/*****************************************************************************/
370
371int filesystem_close(struct webdav_request_close *request_close)
372{
373	int error = 0;
374	struct node_entry *node;
375	Boolean locked = false;
376
377	error = RetrieveDataFromOpaqueID(request_close->obj_id, (void **)&node);
378	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
379
380	/* Trying to close something we did not open? */
381	require_action(NODE_FILE_IS_CACHED(node), not_open, error = EBADF);
382
383	/* Kill any threads that may be downloading data for this file */
384	if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_IN_PROGRESS )
385	{
386		node->file_status |= WEBDAV_DOWNLOAD_TERMINATED;
387	}
388
389	while ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_IN_PROGRESS )
390	{
391		/* wait for the downloading thread to acknowledge that we stopped.*/
392		usleep(10000);	/* 10 milliseconds */
393	}
394
395	/* set the file_inactive_time  */
396	time(&node->file_inactive_time);
397
398	/* if file was written sequentially, clean up the context that's hanging around */
399	if ( node->put_ctx != NULL ) {
400		error = cleanup_seq_write(node->put_ctx);
401		free (node->put_ctx);
402		node->put_ctx = NULL;
403	}
404
405
406	lock_node_cache();
407	locked = true;
408	/* if the file was locked, unlock it and if it is deleted and locked (which should not happen), leave it locked and let the lock expire */
409	if ( node->file_locktoken && !NODE_IS_DELETED(node) )
410	{
411		error = network_unlock_with_nodecache_locked(node);
412
413		unlock_node_cache();
414		locked = false;
415		if ( error == ENOENT )
416		{
417			/* the server says it's gone so delete it and its descendants */
418			(void) nodecache_delete_node(node, TRUE);
419			error = ESTALE;
420			goto bad_obj_id;
421		}
422	}
423	if(locked) {
424		unlock_node_cache();
425	}
426
427
428	/*
429	 * If something went wrong with this file, it was deleted, or it is
430	 * a directory, then remove it from the file cache.
431	 */
432	if ( error ||  NODE_IS_DELETED(node) || (node->node_type == WEBDAV_DIR_TYPE) )
433	{
434		(void)nodecache_remove_file_cache(node);
435	}
436
437not_open:
438bad_obj_id:
439
440	return (error);
441}
442
443/*****************************************************************************/
444
445int filesystem_lookup(struct webdav_request_lookup *request_lookup, struct webdav_reply_lookup *reply_lookup)
446{
447	int error;
448	struct node_entry *node;
449	struct node_entry *parent_node;
450	struct webdav_stat_attr statbuf;
451	int lookup;
452	CFStringRef name_string;
453
454	node = NULL;
455
456	// First make sure the name being looked up is valid UTF-8
457	if ( request_lookup->name != NULL && request_lookup->name_length != 0)
458	{
459		name_string = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)request_lookup->name,
460												request_lookup->name_length,
461												kCFStringEncodingUTF8, false);
462		require_action(name_string != NULL, out, error = EINVAL);
463		CFRelease(name_string);
464	}
465
466	error = RetrieveDataFromOpaqueID(request_lookup->dir_id, (void **)&parent_node);
467	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
468
469	if ( request_lookup->force_lookup )
470	{
471		lookup = TRUE;
472	}
473	else
474	{
475		/* see if we already have a node */
476		error = nodecache_get_node(parent_node, request_lookup->name_length, request_lookup->name, FALSE, FALSE, 0, &node);
477		if ( error )
478		{
479			/* no node, ask the server */
480			lookup = TRUE;
481		}
482		else
483		{
484			/* can we used the cached node? */
485			lookup = !node_attributes_valid(node, request_lookup->pcr.pcr_uid);
486		}
487	}
488
489	if ( lookup )
490	{
491		/* look it up on the server */
492		error = network_lookup(request_lookup->pcr.pcr_uid, parent_node,
493			request_lookup->name, request_lookup->name_length, &statbuf);
494		if ( !error )
495		{
496			/* create a new node */
497			error = nodecache_get_node(parent_node, request_lookup->name_length, request_lookup->name, TRUE, FALSE,
498				S_ISREG(statbuf.attr_stat.st_mode) ? WEBDAV_FILE_TYPE : WEBDAV_DIR_TYPE, &node);
499			if ( !error )
500			{
501				/* network_lookup gets all of the struct stat fields except for st_ino so fill it in here with the fileid of the new node */
502				statbuf.attr_stat.st_ino = node->fileid;
503				/* cache the attributes */
504				error = nodecache_add_attributes(node, request_lookup->pcr.pcr_uid, &statbuf, NULL);
505			}
506		}
507		else if ( (error == ENOENT) && (node != NULL) )
508		{
509			/* the server says it's gone so delete it and its descendants */
510			(void) nodecache_delete_node(node, TRUE);
511			node = NULL;
512		}
513	}
514	else if ( node == NULL )
515	{
516		/* we recently created/read the parent directory so assume the object doesn't exist */
517		error = ENOENT;
518	}
519	/* else use the cache node */
520
521	if ( !error )
522	{
523		/* we have the attributes cached */
524		reply_lookup->obj_id = node->nodeid;
525		reply_lookup->obj_fileid = node->fileid;
526		reply_lookup->obj_type = node->node_type;
527
528		reply_lookup->obj_atime.tv_sec = node->attr_stat_info.attr_stat.st_atimespec.tv_sec;
529		reply_lookup->obj_atime.tv_nsec = node->attr_stat_info.attr_stat.st_atimespec.tv_nsec;
530
531		reply_lookup->obj_mtime.tv_sec = node->attr_stat_info.attr_stat.st_mtimespec.tv_sec;
532		reply_lookup->obj_mtime.tv_nsec = node->attr_stat_info.attr_stat.st_mtimespec.tv_nsec;
533
534		reply_lookup->obj_ctime.tv_sec = node->attr_stat_info.attr_stat.st_ctimespec.tv_sec;
535		reply_lookup->obj_ctime.tv_nsec = node->attr_stat_info.attr_stat.st_ctimespec.tv_nsec;
536
537		reply_lookup->obj_createtime.tv_sec = node->attr_stat_info.attr_create_time.tv_sec;
538		reply_lookup->obj_createtime.tv_nsec = node->attr_stat_info.attr_create_time.tv_nsec;
539
540		reply_lookup->obj_filesize = node->attr_stat_info.attr_stat.st_size;
541	}
542
543bad_obj_id:
544out:
545	return (error);
546}
547
548/*****************************************************************************/
549
550int filesystem_getattr(struct webdav_request_getattr *request_getattr, struct webdav_reply_getattr *reply_getattr)
551{
552	int error;
553	struct node_entry *node;
554	struct webdav_stat_attr statbuf;
555	struct webdav_stat *wstat;
556
557	error = RetrieveDataFromOpaqueID(request_getattr->obj_id, (void **)&node);
558	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
559
560	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
561
562	/* see if we have valid attributes */
563	if ( !node_attributes_valid(node, request_getattr->pcr.pcr_uid) )
564	{
565		/* no... look it up on the server */
566		error = network_getattr( request_getattr->pcr.pcr_uid, node, &statbuf);
567		if ( !error )
568		{
569			/* cache the attributes */
570			error = nodecache_add_attributes(node, request_getattr->pcr.pcr_uid, &statbuf, NULL);
571		}
572	}
573	else
574	{
575		error = 0;
576	}
577
578	if ( !error )
579	{
580		/* we have the attributes cached */
581		wstat = &reply_getattr->obj_attr;
582
583		wstat->st_dev = node->attr_stat_info.attr_stat.st_dev;
584		wstat->st_ino = (webdav_ino_t) node->attr_stat_info.attr_stat.st_ino;
585		wstat->st_mode = node->attr_stat_info.attr_stat.st_mode;
586		wstat->st_nlink = node->attr_stat_info.attr_stat.st_nlink;
587		wstat->st_uid = node->attr_stat_info.attr_stat.st_uid;
588		wstat->st_gid = node->attr_stat_info.attr_stat.st_gid;
589		wstat->st_rdev = node->attr_stat_info.attr_stat.st_rdev;
590
591		wstat->st_atimespec.tv_sec = node->attr_stat_info.attr_stat.st_atimespec.tv_sec;
592		wstat->st_atimespec.tv_nsec = node->attr_stat_info.attr_stat.st_atimespec.tv_nsec;
593
594		wstat->st_mtimespec.tv_sec = node->attr_stat_info.attr_stat.st_mtimespec.tv_sec;
595		wstat->st_mtimespec.tv_nsec = node->attr_stat_info.attr_stat.st_mtimespec.tv_nsec;
596
597		wstat->st_ctimespec.tv_sec = node->attr_stat_info.attr_stat.st_ctimespec.tv_sec;
598		wstat->st_ctimespec.tv_nsec = node->attr_stat_info.attr_stat.st_ctimespec.tv_nsec;
599
600		wstat->st_createtimespec.tv_sec = node->attr_stat_info.attr_create_time.tv_sec;
601		wstat->st_createtimespec.tv_nsec = node->attr_stat_info.attr_create_time.tv_nsec;
602
603		wstat->st_size = node->attr_stat_info.attr_stat.st_size;
604		wstat->st_blocks = node->attr_stat_info.attr_stat.st_blocks;
605		wstat->st_blksize = node->attr_stat_info.attr_stat.st_blksize;
606		wstat->st_flags = node->attr_stat_info.attr_stat.st_flags;
607		wstat->st_gen = node->attr_stat_info.attr_stat.st_gen;
608	}
609
610deleted_node:
611bad_obj_id:
612
613	return (error);
614}
615
616/*****************************************************************************/
617
618int filesystem_statfs(struct webdav_request_statfs *request_statfs,
619		struct webdav_reply_statfs *reply_statfs)
620{
621	int error;
622	time_t thetime;
623	int call_server;
624	struct node_entry *node;
625
626	error = RetrieveDataFromOpaqueID(request_statfs->root_obj_id, (void **)&node);
627	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE); /* this should never fail since the root node never goes away */
628
629	if ( gSuppressAllUI )
630	{
631		/* If we're suppressing UI, we don't need statfs to get quota info from the server. */
632		error = 0;
633	}
634	else
635	{
636		thetime = time(0);
637		if ( thetime != -1 )
638		{
639			/* do we need to call the server? */
640			call_server = (statfs_cache_time == 0) || (thetime > (statfs_cache_time + WEBDAV_STATFS_TIMEOUT));
641		}
642		else
643		{
644			/* if we can't get the time we'll zero thetime and call the server */
645			thetime = 0;
646			call_server = TRUE;
647		}
648
649		if ( call_server )
650		{
651			/* update the cached statfs buffer */
652			error = network_statfs(request_statfs->pcr.pcr_uid, node, &statfs_cache_buffer);
653			if ( !error )
654			{
655				/* update the time the cached statfs buffer was updated */
656				statfs_cache_time = thetime;
657			}
658		}
659		else
660		{
661			error = 0;
662		}
663
664		if ( !error )
665		{
666			reply_statfs->fs_attr.f_bsize = statfs_cache_buffer.f_bsize;
667			reply_statfs->fs_attr.f_iosize = statfs_cache_buffer.f_iosize;
668			reply_statfs->fs_attr.f_blocks = statfs_cache_buffer.f_blocks;
669			reply_statfs->fs_attr.f_bfree = statfs_cache_buffer.f_bfree;
670			reply_statfs->fs_attr.f_bavail = statfs_cache_buffer.f_bavail;
671			reply_statfs->fs_attr.f_files = statfs_cache_buffer.f_files;
672			reply_statfs->fs_attr.f_ffree = statfs_cache_buffer.f_ffree;
673		}
674	}
675
676bad_obj_id:
677
678	return (error);
679}
680
681/*****************************************************************************/
682
683/*
684 * The only errors expected from filesystem_mount are:
685 *		ECANCELED - the user could not authenticate and cancelled the mount
686 *		ENODEV - we could not connect to the server (bad URL, server down, etc.)
687 */
688int filesystem_mount(int *a_mount_args)
689{
690	int error;
691
692	error = network_mount(getuid(), a_mount_args);
693
694	return (error);
695}
696
697/*****************************************************************************/
698
699int filesystem_create(struct webdav_request_create *request_create, struct webdav_reply_create *reply_create)
700{
701	int error;
702	struct node_entry *node;
703	struct node_entry *parent_node;
704	time_t creation_date;
705	int theCacheFile;
706
707	error = RetrieveDataFromOpaqueID(request_create->dir_id, (void **)&parent_node);
708	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
709
710	require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
711
712	error = network_create(request_create->pcr.pcr_uid, parent_node, request_create->name, request_create->name_length, &creation_date);
713
714	// Translate ENOENT to workaround VFS bug:
715	// <rdar://problem/6965993> 10A383: WebDAV FS hangs on open with Microsoft servers (unsupported characters)
716	if (error == ENOENT) {
717		error = EIO;
718	}
719
720	if ( !error )
721	{
722		/*
723		 * we just changed the parent_node so update or remove its attributes
724		 */
725		if ( (creation_date != -1) &&	/* if we know when the creation occurred */
726			 (parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= creation_date) &&	/* and that time is later than what's cached */
727			 node_attributes_valid(parent_node, request_create->pcr.pcr_uid) )	/* and the cache is valid */
728		{
729			/* update the times of the cached attributes */
730			parent_node->attr_stat_info.attr_create_time.tv_sec = creation_date;
731			parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec = creation_date;
732			parent_node->attr_stat_info.attr_stat.st_atimespec = parent_node->attr_stat_info.attr_stat.st_ctimespec = parent_node->attr_stat_info.attr_stat.st_mtimespec;
733			parent_node->attr_time = time(NULL);
734		}
735		else
736		{
737			/* remove the attributes */
738			(void)nodecache_remove_attributes(parent_node);
739		}
740
741		/* Create a node */
742		error = nodecache_get_node(parent_node, request_create->name_length, request_create->name, TRUE, TRUE, WEBDAV_FILE_TYPE, &node);
743		if ( !error )
744		{
745			/* if we have the creation date, we can fill in the attributes because everything else is synthesized and the size is 0 */
746			if ( creation_date != -1 )
747			{
748				struct webdav_stat_attr statbuf;
749
750				bzero((void *)&statbuf, sizeof(struct webdav_stat_attr));
751
752				statbuf.attr_stat.st_dev = 0;
753				statbuf.attr_stat.st_ino = node->fileid;
754				statbuf.attr_stat.st_mode = S_IFREG | S_IRWXU;
755				/* Why 1 for st_nlink?
756				 * Getting the real link count for directories is expensive.
757				 * Setting it to 1 lets FTS(3) (and other utilities that assume
758				 * 1 means a file system doesn't support link counts) work.
759				 */
760				statbuf.attr_stat.st_nlink = 1;
761				statbuf.attr_stat.st_uid = UNKNOWNUID;
762				statbuf.attr_stat.st_gid = UNKNOWNUID;
763				statbuf.attr_stat.st_rdev = 0;
764				statbuf.attr_create_time.tv_sec = creation_date;
765				statbuf.attr_stat.st_mtimespec.tv_sec = creation_date;
766				/* set all times to the last modified time since we cannot get the other times */
767				statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_ctimespec = statbuf.attr_stat.st_mtimespec;
768				statbuf.attr_stat.st_size = 0;	/* we just created it */
769				statbuf.attr_stat.st_blocks = 0;	/* we just created it */
770				statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE;
771				statbuf.attr_stat.st_flags = 0;
772				statbuf.attr_stat.st_gen = 0;
773
774				/* cache the attributes */
775				error = nodecache_add_attributes(node, request_create->pcr.pcr_uid, &statbuf, NULL);
776			}
777
778			/*
779			 * Since we just created the file and we can add an empty cache file
780			 * to the node and set the status to download finished. This will
781			 * save time in the next open.
782			 */
783			theCacheFile = -1;
784			error = get_cachefile(&theCacheFile);
785			if ( error == 0 )
786			{
787				/* add the empty file */
788				error = nodecache_add_file_cache(node, theCacheFile);
789				time(&node->file_validated_time);
790				node->file_status = WEBDAV_DOWNLOAD_FINISHED;
791				/* set the file_inactive_time  */
792				time(&node->file_inactive_time);
793			}
794
795			reply_create->obj_id = node->nodeid;
796			reply_create->obj_fileid = node->fileid;
797		}
798	}
799
800deleted_node:
801bad_obj_id:
802
803	return (error);
804}
805
806/*****************************************************************************/
807
808int filesystem_mkdir(struct webdav_request_mkdir *request_mkdir, struct webdav_reply_mkdir *reply_mkdir)
809{
810	int error;
811	struct node_entry *node;
812	struct node_entry *parent_node;
813	time_t creation_date;
814
815	error = RetrieveDataFromOpaqueID(request_mkdir->dir_id, (void **)&parent_node);
816	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
817
818	require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
819
820	error = network_mkdir(request_mkdir->pcr.pcr_uid, parent_node, request_mkdir->name, request_mkdir->name_length, &creation_date);
821	if ( !error )
822	{
823		/*
824		 * we just changed the parent_node so update or remove its attributes
825		 */
826		if ( (creation_date != -1) &&	/* if we know when the creation occurred */
827			 (parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= creation_date) &&	/* and that time is later than what's cached */
828			 node_attributes_valid(parent_node, request_mkdir->pcr.pcr_uid) )	/* and the cache is valid */
829		{
830			/* update the times of the cached attributes */
831			parent_node->attr_stat_info.attr_create_time.tv_sec = creation_date;
832			parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec = creation_date;
833			parent_node->attr_stat_info.attr_stat.st_atimespec = parent_node->attr_stat_info.attr_stat.st_ctimespec = parent_node->attr_stat_info.attr_stat.st_mtimespec;
834			parent_node->attr_time = time(NULL);
835		}
836		else
837		{
838			/* remove the attributes */
839			(void)nodecache_remove_attributes(parent_node);
840		}
841
842		/* Create a node */
843		error = nodecache_get_node(parent_node, request_mkdir->name_length, request_mkdir->name, TRUE, TRUE, WEBDAV_DIR_TYPE, &node);
844		if ( !error )
845		{
846			/* if we have the creation date, we can fill in the attributes because everything else is synthesized */
847			if ( creation_date != -1 )
848			{
849				struct webdav_stat_attr statbuf;
850
851				bzero((void *)&statbuf, sizeof(struct webdav_stat_attr));
852
853				statbuf.attr_stat.st_dev = 0;
854				statbuf.attr_stat.st_ino = node->fileid;
855				statbuf.attr_stat.st_mode = S_IFDIR | S_IRWXU;
856				/* Why 1 for st_nlink?
857				 * Getting the real link count for directories is expensive.
858				 * Setting it to 1 lets FTS(3) (and other utilities that assume
859				 * 1 means a file system doesn't support link counts) work.
860				 */
861				statbuf.attr_stat.st_nlink = 1;
862				statbuf.attr_stat.st_uid = UNKNOWNUID;
863				statbuf.attr_stat.st_gid = UNKNOWNUID;
864				statbuf.attr_stat.st_rdev = 0;
865				statbuf.attr_create_time.tv_sec = creation_date;
866				statbuf.attr_stat.st_mtimespec.tv_sec = creation_date;
867				/* set all other times to the last modified time since we cannot get the other times */
868				statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_ctimespec = statbuf.attr_stat.st_mtimespec;
869				statbuf.attr_stat.st_size = WEBDAV_DIR_SIZE;	/* fake up the directory size */
870				statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
871				statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE;
872				statbuf.attr_stat.st_flags = 0;
873				statbuf.attr_stat.st_gen = 0;
874
875				/* cache the attributes */
876				error = nodecache_add_attributes(node, request_mkdir->pcr.pcr_uid, &statbuf, NULL);
877			}
878
879			reply_mkdir->obj_id = node->nodeid;
880			reply_mkdir->obj_fileid = node->fileid;
881		}
882	}
883
884deleted_node:
885bad_obj_id:
886
887	return (error);
888}
889
890/*****************************************************************************/
891
892int filesystem_read(struct webdav_request_read *request_read, char **a_byte_addr, size_t *a_size)
893{
894	int error;
895	struct node_entry *node;
896
897	error = RetrieveDataFromOpaqueID(request_read->obj_id, (void **)&node);
898	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
899
900	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
901
902	// Note: request_read->count has already been checked for overflow
903	error = network_read(request_read->pcr.pcr_uid, node,
904		request_read->offset, (size_t)request_read->count, a_byte_addr, a_size);
905
906deleted_node:
907bad_obj_id:
908
909	return ( error );
910}
911
912/*****************************************************************************/
913
914int filesystem_rename(struct webdav_request_rename *request_rename)
915{
916	int error = 0;
917	struct node_entry *f_node;
918	struct node_entry *t_node;
919	struct node_entry *parent_node;
920	time_t rename_date;
921
922	error = RetrieveDataFromOpaqueID(request_rename->from_obj_id, (void **)&f_node);
923	require_noerr_action_quiet(error, bad_from_obj_id, error = ESTALE);
924
925	require_action_quiet(!NODE_IS_DELETED(f_node), deleted_node, error = ESTALE);
926
927	error = RetrieveDataFromOpaqueID(request_rename->to_dir_id, (void **)&parent_node);
928	require_noerr_action_quiet(error, bad_to_dir_id, error = ESTALE);
929
930	require_action_quiet(!NODE_IS_DELETED(parent_node), deleted_node, error = ESTALE);
931
932	if ( request_rename->to_obj_id != kInvalidOpaqueID )
933	{
934		/* "to" exists */
935		error = RetrieveDataFromOpaqueID(request_rename->to_obj_id, (void **)&t_node);
936		require_noerr_action_quiet(error, bad_to_obj_id, error = ESTALE);
937
938		require_action_quiet(!NODE_IS_DELETED(t_node), deleted_node, error = ESTALE);
939
940		/* "from" and "to" must be the same file_type */
941		if ( f_node->node_type != t_node->node_type )
942		{
943			/* return the appropriate error */
944			error = (f_node->node_type == WEBDAV_FILE_TYPE) ? EISDIR : ENOTDIR;
945		}
946		else
947		{
948			error = 0;
949		}
950	}
951	else
952	{
953		t_node = NULL;
954		error = 0;
955	}
956
957	if ( !error )
958	{
959		error = network_rename(request_rename->pcr.pcr_uid, f_node, t_node,
960			parent_node, request_rename->to_name, request_rename->to_name_length, &rename_date);
961		if ( !error )
962		{
963			/*
964			 * we just changed the parent node(s) so update or remove their attributes
965			 */
966			if ( (rename_date != -1) &&	/* if we know when the creation occurred */
967				 (f_node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= rename_date) &&		/* and that time is later than what's cached */
968				 node_attributes_valid(f_node->parent, request_rename->pcr.pcr_uid) )	/* and the cache is valid */
969			{
970				/* update the times of the cached attributes */
971				f_node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec = rename_date;
972				f_node->parent->attr_stat_info.attr_stat.st_atimespec = f_node->parent->attr_stat_info.attr_stat.st_ctimespec = f_node->parent->attr_stat_info.attr_stat.st_mtimespec;
973				f_node->parent->attr_time = time(NULL);
974			}
975			else
976			{
977				/* remove the attributes */
978				(void)nodecache_remove_attributes(f_node->parent);
979			}
980			if ( f_node->parent != parent_node )
981			{
982				if ( (rename_date != -1) &&	/* if we know when the creation occurred */
983					 (parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= rename_date) &&	/* and that time is later than what's cached */
984					 node_attributes_valid(parent_node, request_rename->pcr.pcr_uid) )	/* and the cache is valid */
985				{
986					/* update the times of the cached attributes */
987					parent_node->attr_stat_info.attr_stat.st_mtimespec.tv_sec = rename_date;
988					parent_node->attr_stat_info.attr_stat.st_atimespec = parent_node->attr_stat_info.attr_stat.st_ctimespec = parent_node->attr_stat_info.attr_stat.st_mtimespec;
989					parent_node->attr_time = time(NULL);
990				}
991				else
992				{
993					/* remove the attributes */
994					(void)nodecache_remove_attributes(parent_node);
995				}
996			}
997
998			if ( t_node != NULL )
999			{
1000				if ( nodecache_delete_node(t_node, FALSE) != 0 )
1001				{
1002					debug_string("nodecache_delete_node failed");
1003				}
1004			}
1005
1006			/* move "from" node into the destination directory (with a possible rename) */
1007			if ( nodecache_move_node(f_node, parent_node, request_rename->to_name_length, request_rename->to_name) != 0 )
1008			{
1009				debug_string("nodecache_move_node failed");
1010			}
1011
1012			statfs_cache_time = 0;
1013		}
1014	}
1015
1016deleted_node:
1017bad_to_obj_id:
1018bad_to_dir_id:
1019bad_from_obj_id:
1020
1021	return (error);
1022}
1023
1024/*****************************************************************************/
1025
1026int filesystem_remove(struct webdav_request_remove *request_remove)
1027{
1028	int error;
1029	struct node_entry *node;
1030	time_t remove_date;
1031
1032	error = RetrieveDataFromOpaqueID(request_remove->obj_id, (void **)&node);
1033	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
1034
1035	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
1036
1037	error = network_remove(request_remove->pcr.pcr_uid, node, &remove_date);
1038
1039	/*
1040	 *  When connected to an Mac OS X Server, I can delete the main file (ie blah.dmg), but when I try
1041	 *  to delete the ._ file (ie ._blah.dmg), I get a file not found error since the vfs layer on the
1042	 *  server already deleted the ._ file.  Since I am deleting the ._ anyways, the ENOENT is ok and I
1043	 *  should still clean up.
1044	 */
1045	if ( (!error) || (error == ENOENT) )
1046	{
1047		/*
1048		 * we just changed the parent_node so update or remove its attributes
1049		 */
1050		if ( (remove_date != -1) &&	/* if we know when the creation occurred */
1051			 (node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= remove_date) &&	/* and that time is later than what's cached */
1052			 node_attributes_valid(node->parent, request_remove->pcr.pcr_uid) )	/* and the cache is valid */
1053		{
1054			/* update the times of the cached attributes */
1055			node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec = remove_date;
1056			node->parent->attr_stat_info.attr_stat.st_atimespec = node->parent->attr_stat_info.attr_stat.st_ctimespec = node->parent->attr_stat_info.attr_stat.st_mtimespec;
1057			node->parent->attr_time = time(NULL);
1058		}
1059		else
1060		{
1061			/* remove the attributes */
1062			(void)nodecache_remove_attributes(node->parent);
1063		}
1064
1065		if ( nodecache_delete_node(node, FALSE) != 0 )
1066		{
1067			debug_string("nodecache_delete_node failed");
1068		}
1069
1070		statfs_cache_time = 0;
1071	}
1072
1073deleted_node:
1074bad_obj_id:
1075
1076	return (error);
1077}
1078
1079/*****************************************************************************/
1080
1081int filesystem_rmdir(struct webdav_request_rmdir *request_rmdir)
1082{
1083	int error;
1084	struct node_entry *node;
1085	time_t remove_date;
1086
1087	error = RetrieveDataFromOpaqueID(request_rmdir->obj_id, (void **)&node);
1088	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
1089
1090	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
1091
1092	/*
1093	 * network_rmdir ensures the directory on the server is empty (which is what really matters)
1094	 * before deleting the directory on the server. So, if network_rmdir is successful, then
1095	 * we need to get rid of the directory node and any of its children nodes.
1096	 */
1097	error = network_rmdir(request_rmdir->pcr.pcr_uid, node, &remove_date);
1098	if ( !error )
1099	{
1100		/*
1101		 * we just changed the parent_node so update or remove its attributes
1102		 */
1103		if ( (remove_date != -1) &&	/* if we know when the creation occurred */
1104			 (node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec <= remove_date) &&	/* and that time is later than what's cached */
1105			 node_attributes_valid(node->parent, request_rmdir->pcr.pcr_uid) )	/* and the cache is valid */
1106		{
1107			/* update the times of the cached attributes */
1108			node->parent->attr_stat_info.attr_stat.st_mtimespec.tv_sec = remove_date;
1109			node->parent->attr_stat_info.attr_stat.st_atimespec = node->parent->attr_stat_info.attr_stat.st_ctimespec = node->parent->attr_stat_info.attr_stat.st_mtimespec;
1110			node->parent->attr_time = time(NULL);
1111		}
1112		else
1113		{
1114			/* remove the attributes */
1115			(void)nodecache_remove_attributes(node->parent);
1116		}
1117
1118		/* delete node and any children we *thought* we knew about (some other client must have deleted them on the server) */
1119		if ( nodecache_delete_node(node, TRUE) != 0 )
1120		{
1121			debug_string("nodecache_delete_node failed");
1122		}
1123
1124		statfs_cache_time = 0;
1125	}
1126
1127deleted_node:
1128bad_obj_id:
1129
1130	return (error);
1131}
1132
1133/*****************************************************************************/
1134
1135int filesystem_fsync(struct webdav_request_fsync *request_fsync)
1136{
1137	int error;
1138	struct node_entry *node;
1139	off_t file_length;
1140	time_t file_last_modified;
1141
1142	error = RetrieveDataFromOpaqueID(request_fsync->obj_id, (void **)&node);
1143	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
1144
1145	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
1146
1147	/* Trying to fsync something that's not open? */
1148	require_action(NODE_FILE_IS_CACHED(node), not_open, error = EBADF);
1149
1150	/* The kernel should not send us an fsync until the file is downloaded */
1151	require_action((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED, still_downloading, error = EIO);
1152
1153	error = network_fsync(request_fsync->pcr.pcr_uid, node, &file_length, &file_last_modified);
1154
1155	if ( (file_length == -1) || (file_last_modified == -1) )
1156	{
1157		/* if we didn't get the length or the file_last_modified date, remove its attributes */
1158		(void)nodecache_remove_attributes(node);
1159	}
1160	else
1161	{
1162		/* otherwise, update its attributes */
1163		struct webdav_stat_attr statbuf;
1164
1165		bzero((void *)&statbuf, sizeof(struct webdav_stat_attr));
1166
1167		statbuf.attr_stat.st_dev = 0;
1168		statbuf.attr_stat.st_ino = node->fileid;
1169		statbuf.attr_stat.st_mode = S_IFREG | S_IRWXU;
1170		/* Why 1 for st_nlink?
1171		* Getting the real link count for directories is expensive.
1172		* Setting it to 1 lets FTS(3) (and other utilities that assume
1173		* 1 means a file system doesn't support link counts) work.
1174		*/
1175		statbuf.attr_stat.st_nlink = 1;
1176		statbuf.attr_stat.st_uid = UNKNOWNUID;
1177		statbuf.attr_stat.st_gid = UNKNOWNUID;
1178		statbuf.attr_stat.st_rdev = 0;
1179		/* set all times (except create time) to the last modified time since we cannot get the other times. */
1180		statbuf.attr_stat.st_mtimespec.tv_sec = file_last_modified;
1181		statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_ctimespec = statbuf.attr_stat.st_mtimespec;
1182		statbuf.attr_stat.st_size = file_length;
1183		statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
1184		statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE;
1185		statbuf.attr_stat.st_flags = 0;
1186		statbuf.attr_stat.st_gen = 0;
1187
1188		/* cache the attributes */
1189		error = nodecache_add_attributes(node, request_fsync->pcr.pcr_uid, &statbuf, NULL);
1190	}
1191
1192	/* and we changed the volume so invalidate the statfs cache */
1193	statfs_cache_time = 0;
1194
1195still_downloading:
1196not_open:
1197deleted_node:
1198bad_obj_id:
1199
1200	return ( error );
1201}
1202
1203/*****************************************************************************/
1204
1205// This function sends a write request to the write manager.
1206// When the request offset is zero, this function also intializes the sequential write engine.
1207//
1208int filesystem_write_seq(struct webdav_request_writeseq *request_sq_wr)
1209{
1210	int error;
1211	ssize_t bytesRead;
1212	// size_t totalBytesRead;
1213	off_t totalBytesRead;
1214	off_t nbytes;
1215	struct node_entry *node;
1216	struct stream_put_ctx *ctx = NULL;
1217	struct seqwrite_mgr_req *mgr_req;
1218	struct timespec timeout;
1219	pthread_mutexattr_t mutexattr;
1220
1221	mgr_req = NULL;
1222	error = 0;
1223
1224	error = RetrieveDataFromOpaqueID(request_sq_wr->obj_id, (void **)&node);
1225	require_noerr_action_quiet(error, out1, error = ESTALE);
1226
1227	/* File size is a hint, can't be trusted */
1228	/* number of bytes to be written can be 0, so no need to check */
1229	require_action_quiet(!NODE_IS_DELETED(node), out1, error = ESTALE);
1230
1231	/* Trying to write something that's not open? */
1232	require_action(NODE_FILE_IS_CACHED(node), out1, error = EBADF);
1233
1234	if ( request_sq_wr->offset == 0 ) {
1235		error = setup_seq_write(request_sq_wr->pcr.pcr_uid, node, request_sq_wr->file_len);
1236
1237		// If file is large, turn off data caching during the upload
1238		if( request_sq_wr->file_len > webdavCacheMaximumSize)
1239			fcntl(node->file_fd, F_NOCACHE, 1);
1240	}
1241
1242	if (error) {
1243		syslog(LOG_ERR, "%s: setup_seq_write returned %d\n", __FUNCTION__, error);
1244		goto out1;
1245	}
1246
1247	ctx = node->put_ctx;
1248	if (request_sq_wr->is_retry)
1249		ctx->is_retry = 1;
1250
1251	// syslog(LOG_DEBUG, "%s: entered. offset %llu, count %lu\n", __FUNCTION__, request_sq_wr->offset, request_sq_wr->count);
1252
1253	pthread_mutex_lock(&ctx->ctx_lock);
1254	/* Set request data which is common across all requests for this call */
1255
1256	mgr_req = (struct seqwrite_mgr_req *) malloc (sizeof(struct seqwrite_mgr_req));
1257	if (mgr_req == NULL) {
1258		syslog(LOG_ERR, "%s: malloc of seqwrite_mgr_req failed", __FUNCTION__);
1259		ctx->finalStatus = EIO;
1260		ctx->finalStatusValid = true;
1261		error = EIO;
1262		pthread_mutex_unlock(&ctx->ctx_lock);
1263		goto out1;
1264	}
1265
1266	bzero(mgr_req, sizeof(struct seqwrite_mgr_req));
1267	mgr_req->refCount = 1;	// hold a reference until were done
1268	mgr_req->req = request_sq_wr;
1269	mgr_req->is_retry = request_sq_wr->is_retry;
1270	mgr_req->data = (unsigned char *)malloc(BODY_BUFFER_SIZE); /* 64K */
1271	if ( mgr_req->data == NULL ) {
1272		syslog(LOG_ERR, "%s: malloc of data buffer failed", __FUNCTION__);
1273		ctx->finalStatus = EIO;
1274		ctx->finalStatusValid = true;
1275		error = EIO;
1276		pthread_mutex_unlock(&ctx->ctx_lock);
1277		goto out1;
1278	}
1279
1280	error = pthread_mutexattr_init(&mutexattr);
1281	if (error) {
1282		syslog(LOG_ERR, "%s: init ctx mutexattr failed, error %d", __FUNCTION__, error);
1283		ctx->finalStatus = EIO;
1284		ctx->finalStatusValid = true;
1285		error = EIO;
1286		pthread_mutex_unlock(&ctx->ctx_lock);
1287		goto out1;
1288	}
1289
1290	error = pthread_mutex_init(&mgr_req->req_lock, &mutexattr);
1291	if (error) {
1292		syslog(LOG_ERR, "%s: init ctx_lock failed, error %d", __FUNCTION__, error);
1293		ctx->finalStatus = EIO;
1294		ctx->finalStatusValid = true;
1295		error = EIO;
1296		pthread_mutex_unlock(&ctx->ctx_lock);
1297		goto out1;
1298	}
1299
1300	error = pthread_cond_init(&mgr_req->req_condvar, NULL);
1301	if (error) {
1302		syslog(LOG_ERR, "%s: init ctx_condvar failed, error %d", __FUNCTION__, error);
1303		ctx->finalStatus = EIO;
1304		ctx->finalStatusValid = true;
1305		error = EIO;
1306		pthread_mutex_unlock(&ctx->ctx_lock);
1307		goto out1;
1308	}
1309
1310	if (node->file_fd == -1)
1311	{
1312		/* cache file's no good, today just isn't our day */
1313		syslog(LOG_ERR, "%s: cache file descriptor is -1, failed.", __FUNCTION__ );
1314		ctx->finalStatus = EIO;
1315		ctx->finalStatusValid = true;
1316		error = EIO;
1317		pthread_mutex_unlock(&ctx->ctx_lock);
1318		goto out1;
1319	}
1320
1321	// Seek to the start of this offset
1322	if ( lseek(node->file_fd, request_sq_wr->offset, SEEK_SET) < 0) {
1323			/* seek failed, dammit */
1324			syslog(LOG_ERR, "%s: lseek errno %d, failed.", __FUNCTION__, errno);
1325			ctx->finalStatus = EIO;
1326			ctx->finalStatusValid = true;
1327			error = EIO;
1328			pthread_mutex_unlock(&ctx->ctx_lock);
1329			goto out1;
1330		}
1331
1332	pthread_mutex_unlock(&ctx->ctx_lock);
1333
1334	totalBytesRead = 0, bytesRead = 0;
1335	/* Loop until everything's written or an error occurs */
1336	while( 1 ) {
1337		pthread_mutex_lock(&ctx->ctx_lock);
1338
1339		// if the sequential write was cancelled, get outta dodge
1340		if ( ctx->mgr_status == WR_MGR_DONE || ctx->finalStatusValid == true ) {
1341			// sequential write was cancelled
1342			// syslog(LOG_DEBUG, "%s: WRITESEQ: cancelled at top of loop mgr_status %d, finalStatusValid %d",
1343			//	__FUNCTION__, ctx->mgr_status == WR_MGR_DONE, ctx->finalStatusValid);
1344			error = ctx->finalStatus;
1345			pthread_mutex_unlock(&ctx->ctx_lock);
1346			goto out1;
1347		}
1348
1349		// If we're done reading, break
1350		if ( totalBytesRead >= request_sq_wr->count ) {
1351			pthread_mutex_unlock(&ctx->ctx_lock);
1352			break;
1353		}
1354
1355		nbytes = MIN( request_sq_wr->count - totalBytesRead, BODY_BUFFER_SIZE );
1356
1357		bytesRead = read( node->file_fd, mgr_req->data, (size_t)nbytes );
1358
1359		/* bytesRead < 0 we got an error */
1360		if ( bytesRead < 0 ) {
1361			syslog(LOG_ERR, "%s: read() cache file returned error %d, failed.", __FUNCTION__, errno);
1362			error = errno;
1363			ctx->finalStatus = error;
1364			ctx->finalStatusValid = true;
1365			pthread_mutex_unlock(&ctx->ctx_lock);
1366			goto out1;
1367		}
1368
1369		mgr_req->type = SEQWRITE_CHUNK;
1370		mgr_req->request_done = false;
1371		mgr_req->chunkLen = bytesRead;
1372		mgr_req->chunkWritten = false;
1373		mgr_req->error = 0;
1374
1375		// queue request
1376		if (queue_writemgr_request_locked(ctx, mgr_req) < 0) {
1377			syslog(LOG_ERR, "%s: queue_writemgr_request_locked failed.", __FUNCTION__);
1378			error = EIO;
1379			ctx->finalStatus = error;
1380			ctx->finalStatusValid = true;
1381			pthread_mutex_unlock(&ctx->ctx_lock);
1382			goto out1;
1383		}
1384
1385		pthread_mutex_unlock(&ctx->ctx_lock);
1386
1387		timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_REQUEST_TIMEOUT;
1388		timeout.tv_nsec = 0;
1389
1390		// now wait on condition var until mgr is done with this request
1391		pthread_mutex_lock(&mgr_req->req_lock);
1392		/* wait for request to finish */
1393		while (mgr_req->request_done == false && ctx->finalStatusValid == false) {
1394			error = pthread_cond_timedwait(&mgr_req->req_condvar, &mgr_req->req_lock, &timeout);
1395			if ( error != 0 ) {
1396				syslog(LOG_ERR, "%s: pthread_cond_timedwait returned error %d, failed.", __FUNCTION__, error);
1397				pthread_mutex_lock(&ctx->ctx_lock);
1398				if ( error == ETIMEDOUT ) {
1399					ctx->finalStatus = ETIMEDOUT;
1400					ctx->finalStatusValid = true;
1401				} else {
1402					ctx->finalStatus = EIO;
1403					ctx->finalStatusValid = true;
1404					error = EIO;
1405				}
1406				pthread_mutex_unlock(&ctx->ctx_lock);
1407				pthread_mutex_unlock(&mgr_req->req_lock);
1408				goto out1;
1409			}
1410		}
1411		pthread_mutex_unlock(&mgr_req->req_lock);
1412		totalBytesRead += bytesRead;
1413	}
1414
1415	if (mgr_req->request_done == true) {
1416		error = mgr_req->error;
1417	} else {
1418		error = ctx->finalStatus;
1419	}
1420
1421	// syslog(LOG_ERR, "%s: WRITE_SEQ: Write at offset %llu done, mgr_req.request_done: %d, error %d",
1422	//	__FUNCTION__, request_sq_wr->offset, mgr_req.request_done, error);
1423
1424out1:
1425	if (mgr_req != NULL)
1426		release_writemgr_request(ctx, mgr_req);
1427
1428	if (!error) {
1429		// write succeeded, so turn off retry state
1430		ctx->is_retry = 0;
1431	}
1432
1433	return ( error );
1434}
1435
1436/*****************************************************************************/
1437
1438int filesystem_readdir(struct webdav_request_readdir *request_readdir)
1439{
1440	int error;
1441	struct node_entry *node;
1442
1443	error = RetrieveDataFromOpaqueID(request_readdir->obj_id, (void **)&node);
1444	require_noerr_action_quiet(error, bad_obj_id, error = ESTALE);
1445
1446	require_action_quiet(!NODE_IS_DELETED(node), deleted_node, error = ESTALE);
1447
1448	error = network_readdir(request_readdir->pcr.pcr_uid, request_readdir->cache, node);
1449
1450deleted_node:
1451bad_obj_id:
1452
1453	return (error);
1454}
1455
1456/*****************************************************************************/
1457
1458int filesystem_lock(struct node_entry *node)
1459{
1460	int error;
1461
1462	require_action(node != NULL, null_node, error = EIO);
1463
1464	if ( node->file_locktoken != NULL )
1465	{
1466		error = network_lock(0, TRUE, node); /* uid for refreshes is ignored */
1467	}
1468	else
1469	{
1470		error = 0;
1471	}
1472
1473null_node:
1474
1475	return ( error );
1476}
1477
1478/*****************************************************************************/
1479
1480int filesystem_invalidate_caches(struct webdav_request_invalcaches *request_invalcaches)
1481{
1482	int error;
1483
1484	/* only the owner (mounter) can invalidate */
1485	require_action(request_invalcaches->pcr.pcr_uid == gProcessUID, not_permitted, error = EPERM);
1486
1487	nodecache_invalidate_caches();
1488	error = 0;
1489
1490not_permitted:
1491
1492	return (error);
1493}
1494
1495/*****************************************************************************/
1496