1/*
2 * Copyright (c) 1999-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 * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved
25 */
26/*
27 * Copyright (c) 1992, 1993
28 *	The Regents of the University of California.  All rights reserved.
29 *
30 * The NEXTSTEP Software License Agreement specifies the terms
31 * and conditions for redistribution.
32 *
33 *	@(#)webdav_vnops.c	8.8 (Berkeley) 1/21/94
34 */
35
36#define APPLE_PRIVATE   1 // ����� so we can use sock_nointerrupt()
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/kauth.h>
41#include <sys/ucred.h>
42#include <sys/vnode.h>
43#include <sys/proc.h>
44#include <sys/malloc.h>
45#include <sys/ubc.h>
46#include <sys/fcntl.h>
47#include <sys/dirent.h>
48#include <sys/unistd.h>
49#include <sys/kpi_socket.h>
50#include <sys/mount.h>
51#include <sys/ioccom.h>
52#include <sys/kernel_types.h>
53#include <libkern/libkern.h>
54#include <libkern/OSAtomic.h>
55#include <kern/debug.h>
56
57#include "webdav.h"
58#include "webdav_utils.h"
59
60extern lck_grp_t *webdav_rwlock_group;
61extern lck_mtx_t *webdav_node_hash_mutex;
62
63/*****************************************************************************/
64
65#if 0
66
67static int webdav_print(vnode_t vp)
68{
69	struct webdavnode *pt = VTOWEBDAV(vp);
70
71	/* print a few things from the webdavnode */
72	printf("tag VT_WEBDAV, webdav, id=%ld, obj_ref=%ld, cache_vnode=%ld\n", pt->pt_fileid, pt->pt_obj_ref, pt->pt_cache_vnode);
73	return (0);
74	(void)webdav_print(vp); /* stop complaining if we don't call this function */
75}
76
77__private_extern__
78void log_vnop_start(char *str)
79{
80	struct timespec ts;
81
82	printf("vnop %s start\n", str);
83    ts.tv_sec = 0;
84    ts.tv_nsec = 1 * 1000 * 1000;	/* wait for 1 ms */
85    (void) msleep((caddr_t)&ts, NULL, PCATCH, "log_vnop_start", &ts);
86}
87
88__private_extern__
89void log_vnop_error(char *str, int error)
90{
91	struct timespec ts;
92
93	printf("vnop %s error = %d\n", str, error);
94    ts.tv_sec = 0;
95    ts.tv_nsec = 1 * 1000 * 1000;	/* wait for 1 ms */
96    (void) msleep((caddr_t)&ts, NULL, PCATCH, "log_vnop_error", &ts);
97}
98#endif
99
100/*****************************************************************************/
101
102/*
103 * webdav_copy_creds copies the uid_t from a ucred into a webdav_cred.
104 */
105__private_extern__
106void webdav_copy_creds(vfs_context_t context, struct webdav_cred *dest)
107{
108	dest->pcr_uid = kauth_cred_getuid (vfs_context_ucred(context));
109}
110
111/*****************************************************************************/
112
113/*
114 * webdav_dead is called when the mount_webdav daemon cannot communicate with
115 * the remote WebDAV server and there will be no reconnection attempts.
116 * It uses vfs_event_signal() to tell interested parties the connection with
117 * the server is dead.
118 */
119static
120void webdav_dead(struct webdavmount *fmp)
121{
122	if ( fmp != NULL )
123	{
124		lck_mtx_lock(&fmp->pm_mutex);
125		if ( !(fmp->pm_status & WEBDAV_MOUNT_DEAD) )
126		{
127			fmp->pm_status |= WEBDAV_MOUNT_DEAD;
128			lck_mtx_unlock(&fmp->pm_mutex);
129			printf("webdav server: %s: connection is dead\n", vfs_statfs(fmp->pm_mountp)->f_mntfromname);
130			vfs_event_signal(&vfs_statfs(fmp->pm_mountp)->f_fsid, VQ_DEAD, 0);
131		}
132		else
133			lck_mtx_unlock(&fmp->pm_mutex);
134	}
135}
136
137/*****************************************************************************/
138
139/*
140 * webdav_down is called when the mount_webdav daemon cannot communicate with
141 * the remote WebDAV server. It uses vfs_event_signal() to tell interested
142 * parties the connection with the server is down.
143 */
144static
145void webdav_down(struct webdavmount *fmp)
146{
147	if ( fmp != NULL )
148	{
149		lck_mtx_lock(&fmp->pm_mutex);
150		if ( !(fmp->pm_status & (WEBDAV_MOUNT_TIMEO | WEBDAV_MOUNT_DEAD)) )
151		{
152			fmp->pm_status |= WEBDAV_MOUNT_TIMEO;
153			lck_mtx_unlock(&fmp->pm_mutex);
154			printf("webdav server: %s: not responding\n", vfs_statfs(fmp->pm_mountp)->f_mntfromname);
155			vfs_event_signal(&vfs_statfs(fmp->pm_mountp)->f_fsid, VQ_NOTRESP, 0);
156		}
157		else
158			lck_mtx_unlock(&fmp->pm_mutex);
159	}
160}
161
162/*****************************************************************************/
163
164/*
165 * webdav_up is called when the mount_webdav daemon can communicate with
166 * the remote WebDAV server. It uses vfs_event_signal() to tell interested
167 * parties the connection is OK again if the connection was having problems.
168 */
169
170void webdav_up(struct webdavmount *fmp)
171{
172	if ( fmp != NULL )
173	{
174		lck_mtx_lock(&fmp->pm_mutex);
175        if ( (fmp->pm_status & WEBDAV_MOUNT_TIMEO) )
176		{
177			fmp->pm_status &= ~WEBDAV_MOUNT_TIMEO;
178			lck_mtx_unlock(&fmp->pm_mutex);
179			printf("webdav server: %s: is alive again\n", vfs_statfs(fmp->pm_mountp)->f_mntfromname);
180			vfs_event_signal(&vfs_statfs(fmp->pm_mountp)->f_fsid, VQ_NOTRESP, 1);
181        }
182		else
183			lck_mtx_unlock(&fmp->pm_mutex);
184	}
185}
186
187/*****************************************************************************/
188
189/*
190 * webdav_sendmsg is used to communicate with the userland half of the file
191 * system.
192 *
193 * Inputs:
194 *      vnop        the operation to be peformed -- defined operations
195 *					are in webdav.h
196 *      fmp         pointer to struct webdavmount for the file system
197 *      request     pointer to the webdav_request struct
198 *      requestsize size of request
199 *      vardata     pointer to optional variable length data (the last field of
200 *					request), or NULL if none
201 *      vardatasize size of vardata, or 0 if no vardata
202 *
203 * Outputs:
204 *      result      the result of the operation
205 *      reply       pointer to the webdav_reply struct
206 *      replysize   size of reply
207 */
208__private_extern__
209int webdav_sendmsg(int vnop, struct webdavmount *fmp,
210	void *request, size_t requestsize,
211	void *vardata, size_t vardatasize,
212	int *result, void *reply, size_t replysize)
213{
214	int error = 0;
215	socket_t so;
216	int so_open;
217	struct msghdr msg;
218	struct iovec aiov[3];
219	struct timeval tv;
220	struct timeval lasttrytime;
221	struct timeval currenttime;
222	size_t iolen;
223	uint32_t num_rcv_timeouts;
224
225	if ( fmp == NULL )
226		panic("webdav_sendmsg: fmp is NULL!");
227
228	/* get current time */
229	microtime(&currenttime);
230	so_open = FALSE;
231
232	while ( TRUE )
233	{
234		lasttrytime.tv_sec = currenttime.tv_sec;
235
236		/* make we're not force unmounting */
237		if ( (vnop != WEBDAV_UNMOUNT) && vfs_isforce(fmp->pm_mountp) )
238		{
239			error = ENXIO;
240			break;
241		}
242
243		/* don't open more connections than the user-land server can handle */
244		lck_mtx_lock(&fmp->pm_mutex);
245again:
246		if (fmp->pm_open_connections >= WEBDAV_MAX_KEXT_CONNECTIONS)
247		{
248			fmp->pm_status |= WEBDAV_MOUNT_CONNECTION_WANTED;
249			error = msleep((caddr_t)&fmp->pm_open_connections, &fmp->pm_mutex, PCATCH, "webdav_sendmsg - pm_open_connections", NULL);
250			if ( error )
251			{
252				lck_mtx_unlock(&fmp->pm_mutex);
253				break;
254			}
255			goto again;
256		}
257
258		++fmp->pm_open_connections;
259		lck_mtx_unlock(&fmp->pm_mutex);
260
261		/* create a new socket */
262		error = sock_socket(PF_LOCAL, SOCK_STREAM, 0, NULL, NULL, &so);
263		if ( error != 0 )
264		{
265			printf("webdav_sendmsg: sock_socket() = %d\n", error);
266			so_open = FALSE;
267
268			lck_mtx_lock(&fmp->pm_mutex);
269			--fmp->pm_open_connections;
270			lck_mtx_unlock(&fmp->pm_mutex);
271			break;
272		}
273		else
274		{
275			so_open = TRUE;
276		}
277
278		/* set the socket receive timeout */
279		tv.tv_sec = WEBDAV_SO_RCVTIMEO_SECONDS;
280		tv.tv_usec = 0;
281		error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, (uint32_t)sizeof(struct timeval));
282		if (error)
283		{
284			printf("webdav_sendmsg: sock_setsockopt() = %d\n", error);
285			break;
286		}
287
288		/*
289		 * When sock_connect() is called on local domain sockets, the attach
290		 * code calls soreserve() with hard coded values (currently PIPSIZ -- 8192).
291		 */
292
293		/* make we're not force unmounting */
294		if ( (vnop != WEBDAV_UNMOUNT) && vfs_isforce(fmp->pm_mountp) )
295		{
296			error = ENXIO;
297			break;
298		}
299
300		/* kick off connection */
301		error = sock_connect(so, fmp->pm_socket_name, 0);
302		if (error && error != EINPROGRESS)
303		{
304			/* is the other side gone? If so, we're dead. */
305			if ( error == ECONNREFUSED )
306			{
307				webdav_dead(fmp);
308			}
309			/* ENOENT is expected after a normal unmount */
310			if ( error != ENOENT )
311			{
312				printf("webdav_sendmsg: sock_connect() = %d\n", error);
313			}
314			break;
315		}
316
317		/* disable interrupts on socket buffers */
318		error = sock_nointerrupt(so, TRUE);
319		if (error)
320		{
321			printf("webdav_sendmsg: sock_nointerrupt() = %d\n", error);
322			break;
323		}
324
325		memset(&msg, 0, sizeof(msg));
326
327		aiov[0].iov_base = (caddr_t) & vnop;
328		aiov[0].iov_len = sizeof(vnop);
329		aiov[1].iov_base = (caddr_t)request;
330		aiov[1].iov_len = requestsize;
331		if ( vardatasize == 0 )
332		{
333			msg.msg_iovlen = 2;
334		}
335		else
336		{
337			aiov[2].iov_base = vardata;
338			aiov[2].iov_len = vardatasize;
339			msg.msg_iovlen = 3;
340		}
341		msg.msg_iov = aiov;
342
343		/* make we're not force unmounting */
344		if ( (vnop != WEBDAV_UNMOUNT) && vfs_isforce(fmp->pm_mountp) )
345		{
346			error = ENXIO;
347			break;
348		}
349
350		error = sock_send(so, &msg, 0, &iolen);
351		if (error)
352		{
353			printf("webdav_sendmsg: sock_send() = %d\n", error);
354			break;
355		}
356
357		memset(&msg, 0, sizeof(msg));
358
359		aiov[0].iov_base = (caddr_t)result;
360		aiov[0].iov_len = sizeof(*result);
361		aiov[1].iov_base = (caddr_t)reply;
362		aiov[1].iov_len = replysize;
363		msg.msg_iov = aiov;
364		msg.msg_iovlen = (replysize == 0 ? 1 : 2);
365
366		num_rcv_timeouts = 0;
367		while ( TRUE )
368		{
369			/* make we're not force unmounting */
370			if ( (vnop != WEBDAV_UNMOUNT) && vfs_isforce(fmp->pm_mountp) )
371			{
372				error = ENXIO;
373				break;
374			}
375
376			error = sock_receive(so, &msg, MSG_WAITALL, &iolen);
377
378			/* did sock_receive timeout? */
379			if (error != EWOULDBLOCK)
380			{
381				/* sock_receive did not time out */
382				if ( error != 0 )
383				{
384					printf("webdav_sendmsg: sock_receive() = %d\n", error);
385				}
386				break;
387			}
388			else {
389				/* sock_receive DID time out */
390				if ( (++num_rcv_timeouts == WEBDAV_MAX_SOCK_RCV_TIMEOUTS ) &&
391				     (vnop != WEBDAV_WRITE) && (vnop != WEBDAV_READ) &&
392					 (vnop != WEBDAV_FSYNC) && (vnop != WEBDAV_WRITESEQ) ) {
393						// This vnop has timed out.
394						printf("webdav_sendmsg: sock_receive() timeout. vnop: %d\n", vnop);
395						error = ETIMEDOUT;
396						break;
397				}
398			}
399		}
400		if ( error != 0 )
401		{
402			break;
403		}
404
405		if ( *result & WEBDAV_CONNECTION_DOWN_MASK )
406		{
407			/* communications with mount_webdav were OK, but the remote server is unreachable */
408			if ( fmp->pm_status & WEBDAV_MOUNT_SUPPRESS_ALL_UI )
409			{
410				webdav_dead(fmp);
411			}
412			else
413			{
414				webdav_down(fmp);
415			}
416			*result &= ~WEBDAV_CONNECTION_DOWN_MASK;
417
418			/* If this request failed because of the connection problem, retry */
419			if ( *result == ENXIO && !(fmp->pm_status & WEBDAV_MOUNT_SUPPRESS_ALL_UI))
420			{
421				/* get current time */
422				microtime(&currenttime);
423				if ( currenttime.tv_sec < (lasttrytime.tv_sec + 2) )
424				{
425					struct timespec ts;
426
427					/* sleep for 2 secs before retrying again */
428					ts.tv_sec = 2;
429					ts.tv_nsec = 0;
430					error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_sendmsg", &ts);
431					if ( (error != 0) && (error != EWOULDBLOCK) )
432					{
433						printf("webdav_sendmsg: msleep: %d\n", error);
434						break;
435					}
436					microtime(&currenttime);
437				}
438				/* no break so we'll retry */
439			}
440			else
441			{
442				break;
443			}
444		}
445		else
446		{
447			webdav_up(fmp);
448			break;
449		}
450
451		(void) sock_shutdown(so, SHUT_RDWR); /* ignore failures - nothing can be done */
452		sock_close(so);
453		so_open = FALSE;
454
455		lck_mtx_lock(&fmp->pm_mutex);
456		--fmp->pm_open_connections;
457
458		/* if anyone else is waiting for a connection, wake them up */
459		if ( fmp->pm_status & WEBDAV_MOUNT_CONNECTION_WANTED )
460		{
461			fmp->pm_status &= ~WEBDAV_MOUNT_CONNECTION_WANTED;
462			wakeup((caddr_t)&fmp->pm_open_connections);
463		}
464
465		lck_mtx_unlock(&fmp->pm_mutex);
466
467		/* ... and retry */
468	}
469
470	if ( so_open )
471	{
472		(void) sock_shutdown(so, SHUT_RDWR); /* ignore failures - nothing can be done */
473		sock_close(so);
474		so_open = FALSE;
475
476		lck_mtx_lock(&fmp->pm_mutex);
477		--fmp->pm_open_connections;
478		lck_mtx_unlock(&fmp->pm_mutex);
479	}
480
481	/* if anyone else is waiting for a connection, wake them up */
482	lck_mtx_lock(&fmp->pm_mutex);
483	if ( fmp->pm_status & WEBDAV_MOUNT_CONNECTION_WANTED )
484	{
485		fmp->pm_status &= ~WEBDAV_MOUNT_CONNECTION_WANTED;
486		wakeup((caddr_t)&fmp->pm_open_connections);
487	}
488	lck_mtx_unlock(&fmp->pm_mutex);
489
490	/* translate all unexpected errors to EIO. Leave ENXIO (unmounting) alone. */
491	if ( (error != 0) && (error != ENXIO) && (error != ETIMEDOUT) && (error != EACCES))
492	{
493		error = EIO;
494	}
495
496	return (error);
497}
498
499/*****************************************************************************/
500
501static
502void webdav_purge_stale_vnode(vnode_t vp)
503{
504	/* get the vnode out of the name cache now so subsequent lookups won't find it */
505	cache_purge(vp);
506	/* remove the inode from the hash table */
507	webdav_hashrem(VTOWEBDAV(vp));
508	/* recycle the vnode --  we don't care if the recycle was done or not */
509	(void) vnode_recycle(vp);
510}
511
512/*****************************************************************************/
513
514static
515int webdav_lookup(struct vnop_lookup_args *ap, struct webdav_reply_lookup *reply_lookup)
516{
517	int error;
518	int server_error;
519	struct webdav_request_lookup request_lookup;
520	struct componentname *cnp;
521	int nameiop;
522
523	cnp = ap->a_cnp;
524	nameiop = cnp->cn_nameiop;
525
526	/* set up the request */
527	webdav_copy_creds(ap->a_context, &request_lookup.pcr);
528	request_lookup.dir_id = VTOWEBDAV(ap->a_dvp)->pt_obj_id;
529	/* don't use a cached lookup if the operation is create or rename and this is name being created (or renamed to) */
530	request_lookup.force_lookup = ((nameiop == CREATE || nameiop == RENAME) && (cnp->cn_flags & ISLASTCN));
531	request_lookup.name_length = cnp->cn_namelen;
532
533	server_error = 0;
534	bzero(reply_lookup, sizeof(struct webdav_reply_lookup));
535
536	error = webdav_sendmsg(WEBDAV_LOOKUP, VFSTOWEBDAV(vnode_mount(ap->a_dvp)),
537		&request_lookup, offsetof(struct webdav_request_lookup, name),
538		cnp->cn_nameptr, cnp->cn_namelen,
539		&server_error, reply_lookup, sizeof(struct webdav_reply_lookup));
540	if ( (error == 0) && (server_error != 0) )
541	{
542		if ( server_error == ESTALE )
543		{
544			/*
545			 * The object id(s) passed to userland are invalid.
546			 * Purge the vnode(s) and restart the request.
547			 */
548			webdav_purge_stale_vnode(ap->a_dvp);
549			error = ERESTART;
550		}
551		else
552		{
553			error = server_error;
554		}
555	}
556
557	return ( error );
558}
559
560/*****************************************************************************/
561
562/*
563 * webdav_getattr_common
564 *
565 * Common routine for returning the most up-to-date vattr information.
566 * It is called by webdav_vnop_getattr(), and also needed by webdav_vnop_lookup() for dealing
567 * with negative name caching.
568 *
569 * Note: This routine does not lock the webdavnode associated with vp.
570 *       A shared lock MUST be held on the webdavnode 'vp' before calling this routine.
571 *
572 * results:
573 *	0		Success.
574 *	EIO		A physical I/O error has occurred, or this error was generated for
575 *			implementation-defined reasons.
576 */
577static int webdav_getattr_common(vnode_t vp, struct vnode_attr *vap, vfs_context_t a_context)
578{
579	vnode_t cachevp;
580	struct vnode_attr cache_vap;
581	struct webdavnode *pt;
582	struct webdavmount *fmp;
583	struct timespec ts;
584	int error;
585	int server_error;
586	int cache_vap_valid;
587	int need_attr_times, need_attr_size;
588	struct webdav_request_getattr request_getattr;
589	struct webdav_reply_getattr reply_getattr;
590
591	START_MARKER("webdav_getattr");
592
593	fmp = VFSTOWEBDAV(vnode_mount(vp));
594	pt = VTOWEBDAV(vp);
595
596	/* make sure the file exists */
597	if (pt->pt_status & WEBDAV_DELETED)
598	{
599		error = ENOENT;
600		goto bad;
601	}
602
603	cachevp = pt->pt_cache_vnode;
604	error = server_error = 0;
605	need_attr_times = 0;
606	need_attr_size = 0;
607
608	if ( VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time) ||
609		VATTR_IS_ACTIVE(vap, va_change_time) || VATTR_IS_ACTIVE(vap, va_create_time))
610		need_attr_times = 1;
611
612	if ( VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_iosize) ||
613		VATTR_IS_ACTIVE(vap, va_total_alloc))
614		need_attr_size = 1;
615
616	/* get everything we need out of vp and related structures before
617	 * making any blocking calls where vp could go away.
618	 */
619	/* full access for owner only - the server decides what can really be done */
620	VATTR_RETURN(vap, va_mode, S_IRWXU |	/* owner */
621				   (vnode_isdir(vp) ? S_IFDIR : S_IFREG));
622	/* Why 1 for va_nlink?
623	 * Getting the real link count for directories is expensive.
624	 * Setting it to 1 lets FTS(3) (and other utilities that assume
625	 * 1 means a file system doesn't support link counts) work.
626	 */
627	VATTR_RETURN(vap, va_nlink, 1);
628	VATTR_RETURN(vap, va_uid, fmp->pm_uid);
629	VATTR_RETURN(vap, va_gid, fmp->pm_gid);
630	VATTR_RETURN(vap, va_fsid, vfs_statfs(vnode_mount(vp))->f_fsid.val[0]);
631	VATTR_RETURN(vap, va_fileid, pt->pt_fileid);
632
633	webdav_timespec64_to_timespec(pt->pt_atime, &ts);
634	VATTR_RETURN(vap, va_access_time, ts);
635
636	webdav_timespec64_to_timespec(pt->pt_mtime, &ts);
637	VATTR_RETURN(vap, va_modify_time, ts);
638
639	webdav_timespec64_to_timespec(pt->pt_ctime, &ts);
640	VATTR_RETURN(vap, va_change_time, ts);
641
642	webdav_timespec64_to_timespec(pt->pt_createtime, &ts);
643	VATTR_RETURN(vap, va_create_time, ts);
644
645	VATTR_RETURN(vap, va_gen, 0);
646	VATTR_RETURN(vap, va_flags, 0);
647	VATTR_RETURN(vap, va_rdev, 0);
648	VATTR_RETURN(vap, va_filerev, 0);
649
650	// Check if more attributes are needed
651	if ( (need_attr_times == 0) && (need_attr_size == 0))
652		goto done;
653
654	bzero(&reply_getattr, sizeof(struct webdav_reply_getattr));
655
656	cache_vap_valid = FALSE;
657
658	if ( (vnode_isreg(vp)) && (cachevp != NULLVP) )
659	{
660		/* vp is a file and there's a cache file.
661		 * Get the cache file's information since it is the latest.
662		 */
663		VATTR_INIT(&cache_vap);
664		VATTR_WANTED(&cache_vap, va_flags);
665
666		if (need_attr_size) {
667			VATTR_WANTED(&cache_vap, va_data_size);
668			VATTR_WANTED(&cache_vap, va_total_alloc);
669			VATTR_WANTED(&cache_vap, va_iosize);
670		}
671
672		if (need_attr_times) {
673			VATTR_WANTED(&cache_vap, va_access_time);
674			VATTR_WANTED(&cache_vap, va_modify_time);
675			VATTR_WANTED(&cache_vap, va_change_time);
676			VATTR_WANTED(&cache_vap, va_create_time);
677		}
678
679		error = vnode_getattr(cachevp, &cache_vap, a_context);
680		if (error)
681		{
682			printf("webdav_getattr: cachevp: %d\n", error);
683			goto bad;
684		}
685
686		/* if the cache file is complete and the download succeeded, we don't need to call the server */
687		if ( (cache_vap.va_flags & (UF_NODUMP | UF_APPEND)) == 0 )
688		{
689			cache_vap_valid = TRUE;
690		}
691	}
692
693	if ( cache_vap_valid == FALSE )
694	{
695		/* get the server file's information */
696		webdav_copy_creds(a_context, &request_getattr.pcr);
697		request_getattr.obj_id = pt->pt_obj_id;
698
699		error = webdav_sendmsg(WEBDAV_GETATTR, fmp,
700			&request_getattr, sizeof(struct webdav_request_getattr),
701			NULL, 0,
702			&server_error, &reply_getattr, sizeof(struct webdav_reply_getattr));
703		if ( (error == 0) && (server_error != 0) )
704		{
705			if ( server_error == ESTALE )
706			{
707				/*
708				 * The object id(s) passed to userland are invalid.
709				 * Purge the vnode(s) and restart the request.
710				 */
711				webdav_purge_stale_vnode(vp);
712				error = ERESTART;
713			}
714			else
715			{
716				error = server_error;
717			}
718		}
719		if (error)
720		{
721			goto bad;
722		}
723	}
724
725	// Timestamp Attributes
726	if (need_attr_times) {
727		if ( cache_vap_valid )
728		{
729			/* use the time stamps from the cache file if needed */
730			if ( pt->pt_status & WEBDAV_DIRTY )
731			{
732				VATTR_RETURN(vap, va_access_time, cache_vap.va_access_time);
733				VATTR_RETURN(vap, va_modify_time, cache_vap.va_modify_time);
734				VATTR_RETURN(vap, va_change_time, cache_vap.va_change_time);
735				VATTR_RETURN(vap, va_create_time, cache_vap.va_create_time);
736
737				/* update node cache create time, since we have it */
738				timespec_to_webdav_timespec64(vap->va_create_time, &pt->pt_createtime);
739			}
740			else if ( (pt->pt_status & WEBDAV_ACCESSED) )
741			{
742				/* Though we have not dirtied the file, we have accessed it so
743				 * grab the cache file's access time.
744				 */
745				VATTR_RETURN(vap, va_access_time, cache_vap.va_access_time);
746			}
747		}
748		else {
749			/* no cache file, so use reply_getattr.obj_attr for times if possible */
750			if ( reply_getattr.obj_attr.st_atimespec.tv_sec != 0 )
751			{
752				/* use the server times if they were returned (if the getlastmodified
753				 * property isn't returned by the server, reply_getattr.obj_attr.va_atime will be 0)
754				 */
755				webdav_timespec64_to_timespec(reply_getattr.obj_attr.st_atimespec, &ts);
756				VATTR_RETURN(vap, va_access_time, ts);
757				webdav_timespec64_to_timespec(reply_getattr.obj_attr.st_mtimespec, &ts);
758				VATTR_RETURN(vap, va_modify_time, ts);
759				webdav_timespec64_to_timespec(reply_getattr.obj_attr.st_ctimespec, &ts);
760				VATTR_RETURN(vap, va_change_time, ts);
761			}
762			else
763			{
764				/* otherwise, use the current time */
765				nanotime(&vap->va_access_time);
766				VATTR_SET_SUPPORTED(vap, va_access_time);
767				VATTR_RETURN(vap, va_modify_time, vap->va_access_time);
768				VATTR_RETURN(vap, va_change_time, vap->va_access_time);
769			}
770
771			if (reply_getattr.obj_attr.st_createtimespec.tv_sec) {
772				webdav_timespec64_to_timespec(reply_getattr.obj_attr.st_createtimespec, &ts);
773				VATTR_RETURN(vap, va_create_time, ts);
774
775				/* update node cache create time, since we have it */
776				timespec_to_webdav_timespec64(vap->va_create_time, &pt->pt_createtime);
777			}
778		}
779
780		/* Finally, update node timestamp cache */
781		timespec_to_webdav_timespec64(vap->va_access_time, &pt->pt_atime);
782		timespec_to_webdav_timespec64(vap->va_modify_time, &pt->pt_mtime);
783		timespec_to_webdav_timespec64(vap->va_change_time, &pt->pt_ctime);
784
785		nanotime(&ts);
786		timespec_to_webdav_timespec64(ts, &pt->pt_timestamp_refresh);
787	}  // <=== if (need_attr_times)
788
789	// Filesize attributes
790	if (need_attr_size) {
791		if ( cache_vap_valid ) {
792			/* use the cache file's size info */
793			if (VATTR_IS_ACTIVE(vap, va_data_size))
794				VATTR_RETURN(vap, va_data_size, cache_vap.va_data_size);
795			if (VATTR_IS_ACTIVE(vap, va_total_alloc))
796				VATTR_RETURN(vap, va_total_alloc, cache_vap.va_total_alloc);
797			if (VATTR_IS_ACTIVE(vap, va_iosize))
798				VATTR_RETURN(vap, va_iosize, cache_vap.va_iosize);
799		}
800		else {
801			/* use the server file's size info */
802			if (VATTR_IS_ACTIVE(vap, va_data_size))
803				VATTR_RETURN(vap, va_data_size, reply_getattr.obj_attr.st_size);
804			if (VATTR_IS_ACTIVE(vap, va_total_alloc))
805				VATTR_RETURN(vap, va_total_alloc, reply_getattr.obj_attr.st_blocks * S_BLKSIZE);
806			if (VATTR_IS_ACTIVE(vap, va_iosize))
807				VATTR_RETURN(vap, va_iosize, reply_getattr.obj_attr.st_blksize);
808		}
809	}
810
811bad:
812done:
813    return (error);
814}
815
816/*****************************************************************************/
817
818__private_extern__
819int webdav_get(
820	struct mount *mp,			/* mount point */
821	vnode_t dvp,				/* parent vnode */
822	int markroot,				/* if 1, mark as root vnode */
823	struct componentname *cnp,  /* componentname */
824	opaque_id obj_id,			/* object's opaque_id */
825	webdav_ino_t obj_fileid,	/* object's file ID number */
826	enum vtype obj_vtype,		/* VREG or VDIR */
827	struct webdav_timespec64 obj_atime,  /* time of last access */
828	struct webdav_timespec64 obj_mtime,  /* time of last data modification */
829	struct webdav_timespec64 obj_ctime,  /* time of last file status change */
830	struct webdav_timespec64 obj_createtime,  /* file creation time */
831	off_t obj_filesize,			/* object's filesize */
832	vnode_t *vpp)				/* vnode returned here */
833{
834	int error;
835	uint32_t inserted;
836	struct vnode_fsparam vfsp;
837	vnode_t vp;
838	struct webdavnode *new_pt, *found_pt;
839	struct timespec ts;
840
841	/*
842	 * Create a partially initialized webdavnode 'new_pt' and pass it
843	 * to webdav_hashget().  If webdav_hashget() doesn't find
844	 * the node in the hash table, it will stick 'new_pt' into
845	 * the hash table and return.  Any other webdav_hashget() searching
846	 * for the same node will wait until we are done here, since we settting
847	 * the WEBDAV_INIT flag.
848	 *
849	 * If webdav_hashget() finds the node, then we simply free 'new_pt'.
850	 */
851	MALLOC(new_pt, void *, sizeof(struct webdavnode), M_TEMP, M_WAITOK);
852	if (new_pt == NULL)
853	{
854#if DEBUG
855		panic("webdav_get: MALLOC failed for new webdavnode");
856#endif
857		return ENOMEM;
858	}
859
860	bzero(new_pt, sizeof(struct webdavnode));
861	new_pt->pt_mountp = mp;
862	new_pt->pt_fileid = obj_fileid;
863	SET(new_pt->pt_status, WEBDAV_INIT);
864	lck_rw_init(&new_pt->pt_rwlock, webdav_rwlock_group, LCK_ATTR_NULL);
865
866	/* search the hashtable for the webdavnode */
867	found_pt = webdav_hashget(mp, obj_fileid, new_pt, &inserted);
868
869	if (!inserted)
870	{
871		/* Found the node in our hash.
872		* Free the webdavnode we just allocated
873		* and just return the vnode.
874		*/
875		lck_rw_destroy(&new_pt->pt_rwlock, webdav_rwlock_group);
876		FREE((caddr_t)new_pt, M_TEMP);
877
878		vp = WEBDAVTOV(found_pt);
879
880		if ( cnp->cn_flags & MAKEENTRY )
881			cache_enter(dvp, vp, cnp);
882		*vpp = vp;
883		return (0);
884	}
885
886	/* Didn't find the node, and 'new_pt' was inserted into
887	 * the hash table. Now finish initializing the new webdavnode 'new_pt'.
888	 */
889	new_pt->pt_parent = dvp;
890	new_pt->pt_vnode = NULLVP;
891	new_pt->pt_cache_vnode = NULLVP;
892	new_pt->pt_obj_id = obj_id;
893	new_pt->pt_atime = obj_atime;
894	new_pt->pt_mtime = obj_mtime;
895	new_pt->pt_ctime = obj_ctime;
896	new_pt->pt_createtime = obj_createtime;
897	new_pt->pt_mtime_old = obj_mtime;
898	nanotime(&ts);
899	timespec_to_webdav_timespec64(ts, &new_pt->pt_timestamp_refresh);
900	new_pt->pt_filesize = obj_filesize;
901	new_pt->pt_opencount = 0;
902
903	/* Create the vnode */
904	vfsp.vnfs_mp = mp;
905	vfsp.vnfs_vtype = obj_vtype;
906	vfsp.vnfs_str = webdav_name;
907	vfsp.vnfs_dvp = dvp;
908	vfsp.vnfs_fsnode = new_pt;
909	vfsp.vnfs_vops = webdav_vnodeop_p;
910	vfsp.vnfs_markroot = markroot;
911	vfsp.vnfs_marksystem = 0;	/* webdavfs has no "system" vnodes */
912	vfsp.vnfs_rdev = 0;		/* webdavfs doesn't support block devices */
913	vfsp.vnfs_filesize = obj_filesize;
914	vfsp.vnfs_cnp = cnp;
915	vfsp.vnfs_flags = (dvp && cnp && (cnp->cn_flags & MAKEENTRY)) ? 0 : VNFS_NOCACHE;
916
917	error = vnode_create((uint32_t)VNCREATE_FLAVOR, (uint32_t) VCREATESIZE, &vfsp, &new_pt->pt_vnode);
918
919	if ( error == 0 )
920	{
921		/* Make the webdavnode reference the new vnode */
922		vp = new_pt->pt_vnode;
923		vnode_addfsref(vp);
924		vnode_settag(vp, VT_WEBDAV);
925
926		/* Return it.  We're done. */
927		*vpp = vp;
928
929		/* wake up anyone waiting */
930		lck_mtx_lock(webdav_node_hash_mutex);
931		CLR(new_pt->pt_status, WEBDAV_INIT);
932		if (ISSET(new_pt->pt_status, WEBDAV_WAITINIT))
933		{
934			CLR(new_pt->pt_status, WEBDAV_WAITINIT);
935			wakeup(new_pt);
936		}
937		lck_mtx_unlock(webdav_node_hash_mutex);
938	}
939	else
940	{
941		/* remove the partially inited webdavnode from the hash */
942		webdav_unlock(new_pt);
943		webdav_hashrem(new_pt);
944
945		/* wake up anyone waiting */
946		if (ISSET(new_pt->pt_status, WEBDAV_WAITINIT))
947			wakeup(new_pt);
948
949		/* and free up the memory */
950		lck_rw_destroy(&new_pt->pt_rwlock, webdav_rwlock_group);
951		FREE((caddr_t)new_pt, M_TEMP);
952	}
953
954	return ( error );
955}
956
957/*****************************************************************************/
958
959/*
960 *
961 */
962static int webdav_vnop_lookup(struct vnop_lookup_args *ap)
963/*
964	struct vnop_lookup_args {
965		struct vnodeop_desc *a_desc;
966		vnode_t a_dvp;
967		vnode_t *a_vpp;
968		struct componentname *a_cnp;
969		vfs_context_t a_context;
970	};
971*/
972{
973	vnode_t dvp;
974	vnode_t vp;
975	vnode_t *vpp;
976	struct componentname *cnp;
977	struct webdavmount *fmp;
978	struct timespec curr;		/* for negative name caching */
979	struct vnode_attr vap;		/* for negative name caching */
980	struct webdavnode *pt_dvp;
981	int islastcn;
982	int isdotdot;
983	int isdot;
984	int nameiop;
985	int error;
986	struct webdav_reply_lookup reply_lookup;
987	struct webdavnode *pt;
988
989	START_MARKER("webdav_vnop_lookup");
990
991	vpp = ap->a_vpp;
992	dvp = ap->a_dvp;
993	cnp = ap->a_cnp;
994	nameiop = cnp->cn_nameiop;
995
996	fmp = VFSTOWEBDAV(vnode_mount(dvp));
997	pt_dvp = VTOWEBDAV(dvp);
998
999	*vpp = NULLVP;
1000	islastcn = cnp->cn_flags & ISLASTCN;
1001
1002	/*
1003	 * To print out the name being looked up, use:
1004	 * printf("webdav_vnop_lookup: %*s\n", cnp->cn_namelen, cnp->cn_nameptr);
1005	 */
1006
1007	/* See if we're looking up dot or dotdot */
1008	if ( cnp->cn_flags & ISDOTDOT )
1009	{
1010		isdotdot = TRUE;
1011		isdot = FALSE;
1012	}
1013	else if ( (cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1) )
1014	{
1015		isdotdot = FALSE;
1016		isdot = TRUE;
1017	}
1018	else
1019	{
1020		isdotdot = isdot = FALSE;
1021	}
1022
1023	if ( cnp->cn_namelen > fmp->pm_name_max )
1024	{
1025		error = ENAMETOOLONG;
1026	}
1027	else if ( !vnode_isdir(dvp) )
1028	{
1029		error = ENOTDIR;
1030	}
1031	else if ( isdotdot && (vnode_isvroot(dvp)))
1032	{
1033		printf("webdav_vnop_lookup: invalid '..' from root\n");
1034		error = EIO;
1035	}
1036	else if ( islastcn && vnode_vfsisrdonly(dvp) &&
1037			  (nameiop == DELETE || nameiop == RENAME) )
1038	{
1039		error = EROFS;
1040	}
1041	else
1042	{
1043		/*
1044		 * If dvp has negncache entries, we first determine if dvp has been modified by checking
1045		 * the "modified" timestamp attribute.  If dvp has been modified, then we
1046		 * purge the negncache for dvp before the normal cache_lookup.
1047		 */
1048
1049		webdav_lock(pt_dvp, WEBDAV_SHARED_LOCK);
1050		if (pt_dvp->pt_status & WEBDAV_NEGNCENTRIES)
1051		{
1052			/* check if we need to fetch the latest dvp attributes from the server (to update pt_mtime) */
1053			nanotime(&curr);
1054			if ( (curr.tv_sec - pt_dvp->pt_timestamp_refresh.tv_sec) > TIMESTAMP_CACHE_TIMEOUT)
1055			{
1056				/* fetch latest attributes from the server. */
1057				VATTR_INIT(&vap);
1058				VATTR_WANTED(&vap, va_modify_time);
1059				webdav_getattr_common(dvp, &vap, ap->a_context);
1060			}
1061
1062			/* If dvp has been modified on the server since we last checked */
1063			if (pt_dvp->pt_mtime.tv_sec != pt_dvp->pt_mtime_old.tv_sec)
1064			{
1065				cache_purge_negatives(dvp);
1066				pt_dvp->pt_status &= ~WEBDAV_NEGNCENTRIES;
1067				pt_dvp->pt_mtime_old.tv_sec = pt_dvp->pt_mtime.tv_sec;
1068			}
1069		}
1070		webdav_unlock(pt_dvp);
1071
1072		error = cache_lookup(dvp, vpp, cnp);
1073		switch ( error )
1074		{
1075			case -1: /* positive match */
1076				/* just use the vnode found in the cache -- other calls may get ENOENT but that's OK */
1077				error = 0;
1078				break;  /* with vpp set */
1079
1080			case 0: /* no match in cache (or aged out) */
1081				if ( isdot || isdotdot )
1082				{
1083					/* synthesize the lookup reply for dot and dotdot */
1084					pt = isdot ? pt_dvp : VTOWEBDAV(pt_dvp->pt_parent);
1085					if(pt != NULL) {
1086						reply_lookup.obj_id = pt->pt_obj_id;
1087						reply_lookup.obj_fileid = pt->pt_fileid;
1088						reply_lookup.obj_type = WEBDAV_DIR_TYPE;
1089						reply_lookup.obj_atime = pt->pt_atime;
1090						reply_lookup.obj_mtime = pt->pt_mtime;
1091						reply_lookup.obj_ctime = pt-> pt_ctime;
1092						reply_lookup.obj_createtime = pt->pt_createtime;
1093						reply_lookup.obj_filesize = pt->pt_filesize;
1094					} else {
1095						printf("webdav_vnop_lookup: pt is NULL\n");
1096						error = ENOENT;
1097						break;
1098					}
1099				}
1100				else
1101				{
1102					/* lock parent node */
1103					webdav_lock(pt_dvp, WEBDAV_EXCLUSIVE_LOCK);
1104					pt_dvp->pt_lastvop = webdav_vnop_lookup;
1105
1106					error = webdav_lookup(ap, &reply_lookup);
1107
1108					if ( error != 0 )
1109					{
1110						if ( error != ERESTART )
1111						{
1112							/*
1113							 * If we get here we didn't find the entry we were looking for. But
1114							 * that's ok if we are creating or renaming and are at the end of
1115							 * the pathname.
1116							 */
1117							if ( (nameiop == CREATE || nameiop == RENAME) && islastcn )
1118							{
1119								error = EJUSTRETURN;
1120							}
1121							else
1122							{
1123								/* the lookup failed, return ENOENT */
1124								error = ENOENT;
1125								if ((cnp->cn_flags & MAKEENTRY) &&
1126									(cnp->cn_nameiop != CREATE))
1127								{
1128									/* add a negative entry in the name cache */
1129									cache_enter(dvp, NULL, cnp);
1130									pt_dvp->pt_status |= WEBDAV_NEGNCENTRIES;
1131								}
1132							}
1133						}
1134
1135						webdav_unlock(pt_dvp);
1136						break;	/* break with error != 0 */
1137					}
1138					webdav_unlock(pt_dvp);
1139				}
1140
1141				/* the lookup was OK or wasn't needed (dot or dotdot) */
1142
1143				if ( (nameiop == DELETE) && islastcn )
1144				{
1145					if ( isdot )
1146					{
1147						error = vnode_get(dvp);
1148						if ( error == 0 )
1149						{
1150							*vpp = dvp;
1151						}
1152						break;
1153					}
1154					else if ( isdotdot )
1155					{
1156						vp = pt_dvp->pt_parent;
1157						error = vnode_get(vp);
1158						if ( error == 0 )
1159						{
1160							*vpp = vp;
1161						}
1162						break;
1163					}
1164				}
1165
1166				if ( (nameiop == RENAME) && islastcn )
1167				{
1168					if ( isdot )
1169					{
1170						error = EISDIR;
1171						break;
1172					}
1173					else if ( isdotdot )
1174					{
1175						vp = pt_dvp->pt_parent;
1176						error = vnode_get(vp);
1177						if ( error == 0 )
1178						{
1179							*vpp = vp;
1180						}
1181						break;
1182					}
1183				}
1184
1185				if ( isdot )
1186				{
1187					error = vnode_get(dvp);
1188					if ( error == 0 )
1189					{
1190						*vpp = dvp;
1191					}
1192				}
1193				else if ( isdotdot )
1194				{
1195					vp = pt_dvp->pt_parent;
1196					error = vnode_get(vp);
1197					if ( error == 0 )
1198					{
1199						*vpp = vp;
1200					}
1201				}
1202				else
1203				{
1204					/* lock parent node */
1205					webdav_lock(pt_dvp, WEBDAV_EXCLUSIVE_LOCK);
1206					pt_dvp->pt_lastvop = webdav_vnop_lookup;
1207
1208					error = webdav_get(vnode_mount(dvp), dvp, 0, cnp, reply_lookup.obj_id, reply_lookup.obj_fileid,
1209						(reply_lookup.obj_type == WEBDAV_FILE_TYPE) ? VREG : VDIR,
1210						reply_lookup.obj_atime, reply_lookup.obj_mtime, reply_lookup.obj_ctime,
1211						reply_lookup.obj_createtime, reply_lookup.obj_filesize, vpp);
1212
1213					if ( error == 0)
1214						webdav_unlock(VTOWEBDAV(*vpp));
1215
1216					webdav_unlock(pt_dvp);
1217				}
1218				break;
1219
1220			case ENOENT: /* negative match */
1221				break;
1222
1223			default: /* unexpected error */
1224				printf("webdav_vnop_lookup: unexpected response from cache_lookup: %d\n", error);
1225				break;
1226		}
1227	}
1228	RET_ERR("webdav_vnop_lookup", error);
1229}
1230
1231/*****************************************************************************/
1232
1233static int webdav_vnop_open_locked(struct vnop_open_args *ap)
1234/*
1235	struct vnop_open_args {
1236		struct vnodeop_desc *a_desc;
1237		vnode_t a_vp;
1238		int a_mode;
1239		vfs_context_t a_context;
1240	};
1241*/
1242{
1243	struct webdavnode *pt;
1244	vnode_t vp;
1245	int error, server_error;
1246	struct webdavmount *fmp;
1247	struct open_associatecachefile associatecachefile;
1248	struct webdav_request_open request_open;
1249	struct webdav_reply_open reply_open;
1250
1251	START_MARKER("webdav_vnop_open");
1252
1253	vp = ap->a_vp;
1254	pt = VTOWEBDAV(vp);
1255	fmp = VFSTOWEBDAV(vnode_mount(vp));
1256	error = server_error = 0;
1257
1258	/* If we're already open for a sequential write and another writer comes in,
1259	 * kick him out and return EBUSY.  We'd like to kick out readers too, but
1260	 * Finder opens for read before it opens for write, which means it wouldn't
1261	 * be able to perform a sequential write in its current state.
1262	 */
1263	if (pt->pt_writeseq_enabled && pt->pt_opencount_write && (ap->a_mode & FWRITE))
1264	{
1265#if DEBUG
1266		printf("KWRITESEQ: Write sequential is enabled and another writer came in.\n");
1267#endif
1268		return ( EBUSY );
1269	}
1270
1271	/* If it is already open then just ref the node
1272	 * and go on about our business. Make sure to set
1273	 * the write status if this is read/write open
1274	 */
1275	if (pt->pt_cache_vnode)
1276	{
1277		/* increment the open count */
1278		++pt->pt_opencount;
1279		if ( pt->pt_opencount == 0 )
1280		{
1281			/* don't wrap -- return an error */
1282			--pt->pt_opencount;
1283			return ( ENFILE );
1284		}
1285
1286		/* increment the "open for writing count" */
1287		if (ap->a_mode & FWRITE)
1288			++pt->pt_opencount_write;
1289
1290		/* Set the "dir not loaded" bit if this is a directory, that way
1291		 * readdir will know that it needs to force a directory download
1292		 * even if the first call turns out not to be in the middle of the
1293		 * directory
1294		 */
1295		if (vnode_vtype(vp) == VDIR)
1296		{
1297			pt->pt_status |= WEBDAV_DIR_NOT_LOADED;
1298		}
1299		return (0);
1300	}
1301
1302	request_open.ref = -1;  /* set to -1 so that webdav_release_ref() won't do anything */
1303
1304	webdav_copy_creds(ap->a_context, &request_open.pcr);
1305	request_open.flags = OFLAGS(ap->a_mode);
1306	request_open.obj_id = pt->pt_obj_id;
1307
1308	if ( !vnode_isreg(vp) && !vnode_isdir(vp) )
1309	{
1310		/* This should never happen, but just in case */
1311		error = EFTYPE;
1312		goto dealloc_done;
1313	}
1314
1315	associatecachefile.pid = 0;
1316	associatecachefile.cachevp = NULLVP;
1317	error = webdav_assign_ref(&associatecachefile, &request_open.ref);
1318	if ( error )
1319	{
1320		printf("webdav_vnop_open: webdav_assign_ref didn't work\n");
1321		goto dealloc_done;
1322	}
1323
1324	bzero(&reply_open, sizeof(struct webdav_reply_open));
1325
1326	error = webdav_sendmsg(WEBDAV_OPEN, fmp,
1327		&request_open, sizeof(struct webdav_request_open),
1328		NULL, 0,
1329		&server_error, &reply_open, sizeof(struct webdav_reply_open));
1330	if ( (error == 0) && (server_error != 0) )
1331	{
1332		if ( server_error == ESTALE )
1333		{
1334			/*
1335			 * The object id(s) passed to userland are invalid.
1336			 * Purge the vnode(s) and restart the request.
1337			 */
1338			webdav_purge_stale_vnode(vp);
1339			error = ERESTART;
1340		}
1341		else
1342		{
1343			error = server_error;
1344		}
1345	}
1346
1347	if (error == 0)
1348	{
1349		if (reply_open.pid != associatecachefile.pid)
1350		{
1351			printf("webdav_vnop_open: openreply.pid (%d) != associatecachefile.pid (%d)\n",
1352				reply_open.pid, associatecachefile.pid);
1353			error = EPERM;
1354			goto dealloc_done;
1355		}
1356		pt->pt_cache_vnode = associatecachefile.cachevp;
1357
1358		/* set the open count */
1359		pt->pt_opencount = 1;
1360
1361		if (ap->a_mode & FWRITE)
1362			pt->pt_opencount_write = 1;
1363
1364		/* Set the "dir not loaded" bit if this is a directory, that way
1365		 * readdir will know that it needs to force a directory download
1366		 * even if the first call turns out not to be in the middle of the
1367		 * directory
1368		 */
1369		if (vnode_isdir(vp))
1370		{
1371			pt->pt_status |= WEBDAV_DIR_NOT_LOADED;
1372			/* default to not ask for and to not cache additional directory information */
1373			vnode_setnocache(vp);
1374		}
1375
1376		/* blow away statfs cache */
1377		fmp->pm_statfstime = 0;
1378
1379	}
1380
1381dealloc_done:
1382
1383	webdav_release_ref(request_open.ref);
1384
1385	RET_ERR("webdav_vnop_open_locked", error);
1386}
1387
1388/*****************************************************************************/
1389
1390static int webdav_vnop_open(struct vnop_open_args *ap)
1391/*
1392	struct vnop_open_args {
1393		struct vnodeop_desc *a_desc;
1394		vnode_t a_vp;
1395		int a_mode;
1396		vfs_context_t a_context;
1397	};
1398*/
1399{
1400	struct webdavnode *pt;
1401	vnode_t vp;
1402	int error;
1403
1404	START_MARKER("webdav_vnop_open");
1405
1406	vp = ap->a_vp;
1407	pt = VTOWEBDAV(vp);
1408
1409	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
1410	pt->pt_lastvop = webdav_vnop_open;
1411	error = webdav_vnop_open_locked(ap);
1412	webdav_unlock(pt);
1413
1414	RET_ERR("webdav_vnop_open", error);
1415
1416}
1417
1418/*****************************************************************************/
1419
1420/*
1421 * webdav_fsync
1422 *
1423 * webdav_fsync flushes dirty pages (if any) to the cache file and then if
1424 * the file is dirty, pushes it up to the server.
1425 *
1426 * Callers of this routine must ensure (1) the webdavnode is locked exclusively,
1427 * (2) the file is a regular file, and (3) there's a cache vnode.
1428 *
1429 * results:
1430 *	0		Success.
1431 *	EIO		A physical I/O error has occurred, or this error was generated for
1432 *			implementation-defined reasons.
1433 *	ENOSPC	The server returned 507 Insufficient Storage (WebDAV)
1434 */
1435static int webdav_fsync(struct vnop_fsync_args *ap)
1436/*
1437	struct vnop_fsync_args {
1438		struct vnodeop_desc *a_desc;
1439		vnode_t a_vp;
1440		int a_waitfor;
1441		vfs_context_t a_context;
1442	};
1443*/
1444{
1445	struct webdavnode *pt;
1446	vnode_t vp;
1447	vnode_t cachevp;
1448	int error, server_error;
1449	struct webdavmount *fmp;
1450	struct vnode_attr attrbuf;
1451	struct webdav_request_fsync request_fsync;
1452
1453	vp = ap->a_vp;
1454	pt = VTOWEBDAV(vp);
1455	cachevp = pt->pt_cache_vnode;
1456	fmp = VFSTOWEBDAV(vnode_mount(vp));
1457	error = server_error = 0;
1458
1459	if ( !(pt->pt_status & WEBDAV_DIRTY) ||
1460		 (pt->pt_status & WEBDAV_DELETED) )
1461	{
1462		/* If it's not a file, or there is no pt_cache_vnode, or it's not dirty,
1463		 * or it's been deleted, we have nothing to tell the server to sync.
1464		 */
1465		error = 0;
1466		goto done;
1467	}
1468
1469	/* make sure the file is completely downloaded from the server */
1470	do
1471	{
1472		VATTR_INIT(&attrbuf);
1473		VATTR_WANTED(&attrbuf, va_flags);
1474		VATTR_WANTED(&attrbuf, va_data_size);
1475		error = vnode_getattr(cachevp, &attrbuf, ap->a_context);
1476		if (error)
1477		{
1478			goto done;
1479		}
1480
1481		if (attrbuf.va_flags & UF_NODUMP)
1482		{
1483			struct timespec ts;
1484
1485			/* We are downloading the file and we haven't finished
1486			 * since the user process is going push the entire file
1487			 * back to the server, we'll have to wait until we have
1488			 * gotten all of it. Otherwise we will have inadvertantly
1489			 * pushed back an incomplete file and wiped out the original
1490			 */
1491			ts.tv_sec = 0;
1492			ts.tv_nsec = WEBDAV_WAIT_FOR_PAGE_TIME;
1493			error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_fsync", &ts);
1494			if ( error)
1495			{
1496				if ( error == EWOULDBLOCK )
1497				{
1498					error = 0;
1499				}
1500				else
1501				{
1502					printf("webdav_fsync: msleep(): %d\n", error);
1503					/* convert pseudo-errors to EIO */
1504					if ( error < 0 )
1505					{
1506						error = EIO;
1507					}
1508					goto done;
1509				}
1510			}
1511		}
1512		else
1513		{
1514			/* the file has been downloaded */
1515			if ( pt->pt_filesize != (off_t)attrbuf.va_data_size )
1516			{
1517				/* keep the ubc size up to date */
1518				(void) ubc_setsize(vp, attrbuf.va_data_size); /* ignore failures - nothing can be done */
1519				pt->pt_filesize = (off_t)attrbuf.va_data_size;
1520			}
1521			break;
1522		}
1523	} while ( TRUE );
1524
1525	/*
1526	 * The ubc_msync() call can be expensive.
1527	 * There is a fixed cpu cost involved that is directly proportional
1528	 * to the size of file.
1529	 * For a webdav vnode, we do not cache any file data in the VM unless
1530	 * the file is mmap()ed. So if the file was never mapped, there is
1531	 * no need to call ubc_msync().
1532	 */
1533	if ( pt->pt_status & WEBDAV_WASMAPPED )
1534	{
1535		/* This is where we need to tell UBC to flush out all of
1536		 * the dirty pages for this vnode. If we do that then our write
1537		 * and pageout routines will get called if anything needs to
1538		 * be written.  That will cause the status to be dirty if
1539		 * it needs to be marked as such.
1540		 * Note: ubc_msync() returns 0 on success, an errno on failure.
1541		 */
1542		off_t current_size;
1543
1544		current_size = ubc_getsize(vp);
1545		if ( current_size != 0 )
1546		{
1547			if ( (error  = ubc_msync(vp, (off_t)0, current_size, NULL, UBC_PUSHDIRTY | UBC_SYNC)))
1548			{
1549#if DEBUG
1550				printf("webdav_fsync: ubc_msync failed, error %d\n", error);
1551#endif
1552				error = EIO;
1553				goto done;
1554			}
1555		}
1556	}
1557
1558	if ( attrbuf.va_flags & UF_APPEND )
1559	{
1560		/* If the UF_APPEND flag is set, there was an error downloading the file from the
1561		 * server, so exit with an EIO result.
1562		 */
1563		error = EIO;
1564		goto done;
1565	}
1566
1567	/* At this point, the file is completely downloaded into cachevp.
1568	 * Locking cachevp isn't needed because webdavfs vnops are only writer
1569	 * to cachevp after it is downloaded.
1570	 */
1571
1572	/* clear the dirty flag before pushing this to the server */
1573	pt->pt_status &= ~WEBDAV_DIRTY;
1574
1575	webdav_copy_creds(ap->a_context, &request_fsync.pcr);
1576	request_fsync.obj_id = pt->pt_obj_id;
1577
1578	error = webdav_sendmsg(WEBDAV_FSYNC, fmp,
1579		&request_fsync, sizeof(struct webdav_request_fsync),
1580		NULL, 0,
1581		&server_error, NULL, 0);
1582	if ( (error == 0) && (server_error != 0) )
1583	{
1584		if ( server_error == ESTALE )
1585		{
1586			/*
1587			 * The object id(s) passed to userland are invalid.
1588			 * Purge the vnode(s) and restart the request.
1589			 */
1590			webdav_purge_stale_vnode(vp);
1591			error = ERESTART;
1592		}
1593		else
1594		{
1595			error = server_error;
1596		}
1597	}
1598
1599done:
1600	if (!error)
1601		fmp->pm_statfstime = 0;
1602
1603	return ( error );
1604}
1605
1606/*****************************************************************************/
1607
1608/*
1609 * webdav_vnop_fsync
1610 *
1611 * webdav_vnop_fsync flushes dirty pages (if any) to the cache file and then if
1612 * the file is dirty, pushes it up to the server.
1613 *
1614 * results:
1615 *	0		Success.
1616 *	EIO		A physical I/O error has occurred, or this error was generated for
1617 *			implementation-defined reasons.
1618 *	ENOSPC	The server returned 507 Insufficient Storage (WebDAV)
1619 */
1620static int webdav_vnop_fsync(struct vnop_fsync_args *ap)
1621/*
1622	struct vnop_fsync_args {
1623		struct vnodeop_desc *a_desc;
1624		vnode_t a_vp;
1625		int a_waitfor;
1626		vfs_context_t a_context;
1627	};
1628*/
1629{
1630	struct webdavnode *pt;
1631	int error;
1632
1633	START_MARKER("webdav_vnop_fsync");
1634
1635	pt = VTOWEBDAV(ap->a_vp);
1636
1637	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
1638	pt->pt_lastvop = webdav_vnop_fsync;
1639
1640	if ( (pt->pt_cache_vnode != NULLVP) && vnode_isreg(ap->a_vp) )
1641	{
1642		error = webdav_fsync(ap);
1643	}
1644	else
1645	{
1646		error = 0;
1647	}
1648	webdav_unlock(pt);
1649
1650	RET_ERR("webdav_vnop_fsync", error);
1651}
1652
1653/*****************************************************************************/
1654
1655/*
1656	webdav_close_mnomap is the common subroutine used by webdav_vnop_close and
1657	webdav_vnop_mnomap to synchronize the file with the server if needed, and
1658	release the cache vnode if needed.
1659
1660	Note: It is assumed that the webdavnode has been locked by caller
1661
1662	results
1663		0		no error
1664		EIO		an I/O error occurred
1665		ENOSPC	No space left on device/server (from fsync)
1666*/
1667static
1668int webdav_close_mnomap(vnode_t vp, vfs_context_t context, int force_fsync)
1669{
1670	int error;
1671	int server_error;
1672	int fsync_error;
1673	struct webdavnode *pt;
1674
1675	pt = VTOWEBDAV(vp);
1676
1677	/* no errors yet */
1678	error = server_error = fsync_error = 0;
1679
1680	/* If there is no cache file, then we have nothing to tell the server to close. */
1681	if ( pt->pt_cache_vnode != NULLVP )
1682	{
1683		/*
1684		 * If this is a file synchronize the file with the server (if needed).
1685		 * Skip the fsync if file is in sequential write mode.
1686		 * Always call webdav_fsync on close requests with write access (force_fsync)
1687		 * and when the file is not open (at last close, and from mnomap after
1688		 * last close).
1689		 */
1690		if ( vnode_isreg(vp) && (pt->pt_writeseq_enabled == 0) && (force_fsync || (pt->pt_opencount == 0)) )
1691		{
1692			struct vnop_fsync_args fsync_args;
1693
1694			fsync_args.a_vp = vp;
1695			fsync_args.a_waitfor = MNT_WAIT;
1696			fsync_args.a_context = context;
1697			fsync_error = webdav_fsync(&fsync_args);
1698			if ( fsync_error == ERESTART )
1699			{
1700				goto done;
1701			}
1702		}
1703
1704		/*
1705		 * We return errors from fsync no matter what since errors from fsync
1706		 * mean the data was not correctly written in userland.
1707		 */
1708
1709		/* If this the last close and we're not mapped, tell mount_webdav */
1710		if ( (pt->pt_opencount == 0) && !(pt->pt_status & WEBDAV_ISMAPPED) )
1711		{
1712			struct webdav_request_close request_close;
1713			vnode_t temp;
1714
1715			webdav_copy_creds(context, &request_close.pcr);
1716			request_close.obj_id = pt->pt_obj_id;
1717
1718			error = webdav_sendmsg(WEBDAV_CLOSE, VFSTOWEBDAV(vnode_mount(vp)),
1719				&request_close, sizeof(struct webdav_request_close),
1720				NULL, 0,
1721				&server_error, NULL, 0);
1722			if ( (error == 0) && (server_error != 0) )
1723			{
1724				if ( server_error == ESTALE )
1725				{
1726					/*
1727					 * The object id(s) passed to userland are invalid.
1728					 * Purge the vnode(s) and restart the request.
1729					 */
1730					webdav_purge_stale_vnode(vp);
1731					error = ERESTART;
1732					goto done;
1733				}
1734				else
1735				{
1736					error = server_error;
1737				}
1738			}
1739
1740			/* zero out pt_cache_vnode and then release the cache vnode */
1741			temp = pt->pt_cache_vnode;
1742			pt->pt_cache_vnode = NULLVP;
1743			vnode_rele(temp);   /* reference taken in webdav_sysctl() */
1744		}
1745	}
1746	else
1747	{
1748		/* no cache file. Why? */
1749		printf("webdav_close_mnomap: no cache file\n");
1750	}
1751
1752done:
1753
1754	/* report any errors */
1755	if ( error == 0 )
1756	{
1757		/* fsync errors are more important to report than close errors */
1758		if ( fsync_error != 0 )
1759		{
1760			error = fsync_error;
1761		}
1762	}
1763
1764	RET_ERR("webdav_close_mnomap", error);
1765}
1766
1767/*****************************************************************************/
1768
1769/*
1770 * webdav_vnop_close
1771 */
1772static int webdav_vnop_close_locked(struct vnop_close_args *ap)
1773/*
1774	struct vnop_close_args {
1775		struct vnodeop_desc *a_desc;
1776		vnode_t a_vp;
1777		int a_fflag;
1778		vfs_context_t a_context;
1779	};
1780*/
1781{
1782	struct webdavnode *pt;
1783	int force_fsync;
1784	int error = 0;
1785
1786	START_MARKER("webdav_vnop_close_locked");
1787
1788	pt = VTOWEBDAV(ap->a_vp);
1789
1790	/*
1791	 * If this is a file and the corresponding open had write access,
1792	 * synchronize the file with the server (if needed). This must be done from
1793	 * close because this is the last chance we have to return an error to
1794	 * the file system client.
1795	 */
1796	force_fsync = (((ap->a_fflag & FWRITE) != 0) && vnode_isreg(ap->a_vp) && (pt->pt_writeseq_enabled == 0));
1797
1798	/* decrement the open count */
1799	--pt->pt_opencount;
1800
1801	error = webdav_close_mnomap(ap->a_vp, ap->a_context, force_fsync);
1802
1803	// We do this after calling webdav_close_mnomap(), because
1804	// webdav_close_mnomap() will cause an fsync if pt_opencount_write
1805	// is not set.
1806	if (ap->a_fflag & FWRITE) {
1807		/* decrement open for writing count */
1808		--pt->pt_opencount_write;
1809		pt->pt_writeseq_enabled = 0;
1810	}
1811
1812	RET_ERR("webdav_vnop_close_locked", error);
1813}
1814
1815/*****************************************************************************/
1816
1817static int webdav_vnop_close(struct vnop_close_args *ap)
1818/*
1819	struct vnop_close_args {
1820		struct vnodeop_desc *a_desc;
1821		vnode_t a_vp;
1822		int a_fflag;
1823		vfs_context_t a_context;
1824	};
1825*/
1826{
1827	struct webdavnode *pt;
1828	int error = 0;
1829
1830	START_MARKER("webdav_vnop_close");
1831
1832	pt = VTOWEBDAV(ap->a_vp);
1833	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
1834	pt->pt_lastvop = webdav_vnop_close;
1835	webdav_vnop_close_locked(ap);
1836	webdav_unlock(pt);
1837
1838	RET_ERR("webdav_vnop_close", error);
1839}
1840/*****************************************************************************/
1841
1842static int webdav_vnop_mmap(struct vnop_mmap_args *ap)
1843/*
1844	struct vnop_mmap_args {
1845		struct vnodeop_desc *a_desc;
1846		vnode_t a_vp;
1847		int a_fflags;
1848		vfs_context_t a_context;
1849	};
1850*/
1851{
1852	struct webdavnode *pt;
1853
1854	/* mark this file as mapped */
1855	START_MARKER("webdav_vnop_mmap");
1856
1857	pt = VTOWEBDAV(ap->a_vp);
1858	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
1859	pt->pt_lastvop = webdav_vnop_mmap;
1860	pt->pt_status |= (WEBDAV_ISMAPPED | WEBDAV_WASMAPPED);
1861	webdav_unlock(pt);
1862
1863	RET_ERR("webdav_vnop_mmap", 0);
1864}
1865
1866/*****************************************************************************/
1867
1868static int webdav_vnop_mnomap(struct vnop_mnomap_args *ap)
1869/*
1870	struct vnop_mnomap_args {
1871		struct vnodeop_desc *a_desc;
1872		vnode_t a_vp;
1873		vfs_context_t a_context;
1874	};
1875*/
1876{
1877	struct webdavnode *pt;
1878	int error;
1879
1880	START_MARKER("webdav_vnop_mnomap");
1881
1882	pt = VTOWEBDAV(ap->a_vp);
1883	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
1884	pt->pt_lastvop = webdav_vnop_mnomap;
1885
1886	/* mark this file as unmapped */
1887	pt->pt_status &= ~WEBDAV_ISMAPPED;
1888
1889	/* if the file is not open, this will fsync it and close it in user-land */
1890	error = webdav_close_mnomap(ap->a_vp, ap->a_context, FALSE);
1891	webdav_unlock(pt);
1892
1893	RET_ERR("webdav_vnop_mnomap", error);
1894}
1895
1896/*****************************************************************************/
1897
1898/*
1899 * webdav_read_bytes
1900 *
1901 * webdav_read_bytes is called by webdav_vnop_read and webdav_vnop_pagein to
1902 * read bytes directly from the server when we haven't yet downloaded the
1903 * part of the file needed to retrieve the data. If this routine returns an
1904 * error, then the caller will just spin wait for the part of the file needed
1905 * to be downloaded.
1906 *
1907 * results:
1908 * 0	no error - bytes were read
1909 * !0	the bytes were not read and the caller must wait for the download
1910 *
1911 * To do:
1912 *		Pass in current file size so that this routine can compare against
1913 *		WEBDAV_WAIT_IF_WITHIN instead of the caller.
1914 */
1915static int webdav_read_bytes(vnode_t vp, uio_t a_uio, vfs_context_t context)
1916{
1917	int error;
1918	int server_error;
1919	struct webdavnode *pt;
1920	void *buffer;
1921	struct webdavmount *fmp;
1922	struct webdav_request_read request_read;
1923
1924	pt = VTOWEBDAV(vp);
1925	error = server_error = 0;
1926	fmp = VFSTOWEBDAV(vnode_mount(vp));
1927
1928	/* don't bother if the range starts at offset 0 */
1929	if ( (uio_offset(a_uio) == 0) )
1930	{
1931		/* return an error so the caller will wait */
1932		error = EINVAL;
1933		goto done;
1934	}
1935
1936	/* Now allocate the buffer that we are going to use to hold the data that
1937	 * comes back
1938	 */
1939	request_read.count = uio_resid(a_uio); /* this won't overflow because we've already checked uio_resid's size */
1940
1941	MALLOC(buffer, void *, (uint32_t) request_read.count, M_TEMP, M_WAITOK);
1942	if (buffer == NULL)
1943	{
1944#if DEBUG
1945		panic("webdav_read_bytes: MALLOC failed for data buffer");
1946#endif
1947		error = ENOMEM;
1948		goto done;
1949	}
1950
1951	webdav_copy_creds(context, &request_read.pcr);
1952	request_read.obj_id = pt->pt_obj_id;
1953	request_read.offset = uio_offset(a_uio);
1954
1955	error = webdav_sendmsg(WEBDAV_READ, fmp,
1956		&request_read, sizeof(struct webdav_request_read),
1957		NULL, 0,
1958		&server_error, buffer, (size_t)request_read.count);
1959	if ( (error == 0) && (server_error != 0) )
1960	{
1961		if ( server_error == ESTALE )
1962		{
1963			/*
1964			 * The object id(s) passed to userland are invalid.
1965			 * Purge the vnode(s) and restart the request.
1966			 */
1967			webdav_purge_stale_vnode(vp);
1968			error = ERESTART;
1969		}
1970		else
1971		{
1972			error = server_error;
1973		}
1974	}
1975	if (error)
1976	{
1977		/* return an error so the caller will wait */
1978		goto dealloc_done;
1979	}
1980
1981	error = uiomove((caddr_t)buffer, (int)request_read.count, a_uio);
1982
1983dealloc_done:
1984
1985	FREE((void *)buffer, M_TEMP);
1986
1987done:
1988
1989	return ( error );
1990}
1991
1992/*****************************************************************************/
1993
1994/*
1995 * webdav_rdwr
1996 *
1997 * webdav_rdwr is called by webdav_vnop_read and webdav_vnop_write. What webdav_vnop_read and
1998 * webdav_vnop_write need to do is so similar that a common subroutine can be used
1999 * for both. The "reading" flag is used in the few places where read and write
2000 * code is different.
2001 *
2002 * results:
2003 *	0		Success.
2004 *	EISDIR	Tried to read a directory.
2005 *	EIO		A physical I/O error has occurred, or this error was generated for
2006 *			implementation-defined reasons.
2007 */
2008static int webdav_rdwr(struct vnop_read_args *ap)
2009/*
2010	struct vnop_read_args {
2011		struct vnodeop_desc *a_desc;
2012		vnode_t a_vp;
2013		struct uio *a_uio;
2014		int a_ioflag;
2015		vfs_context_t a_context;
2016	};
2017*/
2018{
2019	struct webdavnode *pt;
2020	vnode_t cachevp;
2021	int error;
2022	upl_t upl;
2023	uio_t in_uio;
2024	struct vnode_attr attrbuf;
2025	off_t total_xfersize;
2026	kern_return_t kret;
2027	vnode_t vp;
2028	int mapped_upl;
2029	int file_changed;
2030	int reading;
2031	int tried_bytes;
2032	int ioflag;
2033
2034	vp = ap->a_vp;
2035	pt = VTOWEBDAV(vp);
2036	cachevp = pt->pt_cache_vnode;
2037	in_uio = ap->a_uio;
2038	reading = (uio_rw(in_uio) == UIO_READ);
2039	file_changed = FALSE;
2040	total_xfersize = 0;
2041	tried_bytes = FALSE;
2042	ioflag = ap->a_ioflag;
2043
2044	/* make sure this is not a directory */
2045	if ( vnode_isdir(vp) )
2046	{
2047		error = EISDIR;
2048		goto exit;
2049	}
2050
2051	/* make sure there's a cache file vnode associated with the webdav vnode */
2052	if ( cachevp == NULLVP )
2053	{
2054#if DEBUG
2055		printf("webdav_rdwr: about to %s a uncached vnode\n", (reading ? "read from" : "write to"));
2056#endif
2057		error = EIO;
2058		goto exit;
2059	}
2060
2061	if ( reading )
2062	{
2063		/* we've access the file */
2064		pt->pt_status |= WEBDAV_ACCESSED;
2065	}
2066
2067	/* Start the sleep loop to wait on the background download. We will know that the webdav user
2068	 * process is finished when it either clears the nodump flag or sets the append only flag
2069	 * (indicating an error)
2070	 */
2071	do
2072	{
2073		off_t rounded_iolength;
2074
2075		/* get the cache file's size and va_flags */
2076		VATTR_INIT(&attrbuf);
2077		VATTR_WANTED(&attrbuf, va_flags);
2078		VATTR_WANTED(&attrbuf, va_data_size);
2079		error = vnode_getattr(cachevp, &attrbuf, ap->a_context);
2080		if ( error )
2081		{
2082			goto unlock_exit;
2083		}
2084
2085		/* Don't attempt I/O until either:
2086		 *    (a) the page containing the end of the I/O is in has been downloaded, or
2087		 *    (b) the entire file has been downloaded.
2088		 * This ensures we don't read partially downloaded data, or write into a
2089		 * a portion of the file that is still being downloaded.
2090		 */
2091		rounded_iolength = (off_t)round_page_64(uio_offset(in_uio) + uio_resid(in_uio));
2092
2093		if ( (attrbuf.va_flags & UF_NODUMP) &&
2094			 ( (!(reading) && (ioflag & IO_APPEND)) || (rounded_iolength > (off_t)attrbuf.va_data_size) ) )
2095		{
2096			struct timespec ts;
2097
2098			/* We are downloading the file and we haven't gotten to
2099			 * to the bytes we need so sleep, and then check again.
2100			 */
2101
2102			/* if reading, we may be able to read the part of the file we need out-of-band */
2103			if ( reading )
2104			{
2105				if ( !tried_bytes )
2106				{
2107					if ( rounded_iolength > ((off_t)attrbuf.va_data_size + WEBDAV_WAIT_IF_WITHIN) )
2108					{
2109						/* We aren't close to getting to the part of the file that contains
2110						 * the data we want so try to ask the server for the bytes
2111						 * directly. If that does not work, wait until the stuff gets down.
2112						 */
2113						error = webdav_read_bytes(vp, in_uio, ap->a_context);
2114						if ( !error )
2115						{
2116							/* we're done */
2117							goto exit;
2118						}
2119					}
2120					/* If we are here, we must have failed to get the bytes so return and
2121					* set tried_bytes so we won't attempt that again and sleep */
2122					tried_bytes = TRUE;
2123				}
2124			}
2125
2126			/* sleep for a bit */
2127			ts.tv_sec = 0;
2128			ts.tv_nsec = WEBDAV_WAIT_FOR_PAGE_TIME;
2129			error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_rdwr", &ts);
2130			if ( error)
2131			{
2132				if ( error == EWOULDBLOCK )
2133				{
2134					error = 0;
2135				}
2136				else
2137				{
2138#if DEBUG
2139					printf("webdav_rdwr: msleep(): %d\n", error);
2140#endif
2141					/* convert pseudo-errors to EIO */
2142					if ( error < 0 )
2143					{
2144						error = EIO;
2145					}
2146					goto exit;
2147				}
2148			}
2149		}
2150		else
2151		{
2152			/* the part we need has been downloaded */
2153			if ( pt->pt_filesize < (off_t)attrbuf.va_data_size )
2154			{
2155				(void) ubc_setsize(vp, attrbuf.va_data_size); /* ignore failures - nothing can be done */
2156				pt->pt_filesize = (off_t)attrbuf.va_data_size;
2157			}
2158			break; /* out of while (TRUE) loop */
2159		}
2160	} while ( TRUE );
2161
2162	if ( attrbuf.va_flags & UF_APPEND )
2163	{
2164		/* If the UF_APPEND flag is set, there was an error downloading the file from the
2165		 * server, so exit with an EIO result.
2166		 */
2167		error = EIO;
2168		goto unlock_exit;
2169	}
2170
2171	/* At this point, cachevp is locked and either the file is completely downloaded into
2172	 * cachevp, or the page this I/O ends within has been completely downloaded into cachevp.
2173	 */
2174
2175	/* Determine the total_xfersize. Reads must be within the current file;
2176	 * Writes can extend the file.
2177	 */
2178	if ( reading )
2179	{
2180		/* pin total_xfersize to EOF */
2181		if ( uio_offset(in_uio) > (off_t)attrbuf.va_data_size )
2182		{
2183			total_xfersize = 0;
2184		}
2185		else
2186		{
2187			total_xfersize = MIN(uio_resid(in_uio), ((off_t)attrbuf.va_data_size - uio_offset(in_uio)));
2188			/* make sure total_xfersize isn't negative */
2189			if ( total_xfersize < 0 )
2190			{
2191				total_xfersize = 0;
2192			}
2193		}
2194	}
2195	else
2196	{
2197		/* get total_xfersize and make sure it isn't negative */
2198		total_xfersize = (uio_resid(in_uio) < 0) ? (0) : (uio_resid(in_uio));
2199	}
2200
2201	/*
2202	 * For a webdav vnode, we do not cache any file data in the VM unless
2203	 * the file is mmap()ed. So if the file was never mapped, there is
2204	 * no need to create a upl, scan for valid pages, etc, and the VOP_READ/WRITE
2205	 * to cachevp can handle the request completely.
2206	 */
2207	if ( pt->pt_status & WEBDAV_WASMAPPED )
2208	{
2209		/* If the ubc info exists we may need to get some or all of the data
2210		 * from mapped pages. */
2211
2212		/* loop until total_xfersize has been transferred or error occurs */
2213		while ( total_xfersize > 0 )
2214		{
2215			int currentPage;
2216			int pagecount;
2217			off_t pageOffset;
2218			off_t xfersize;
2219			vm_offset_t addr;
2220			upl_page_info_t *pl;
2221
2222			/* Determine the offset into the first page and how much to transfer this time.
2223			 * xfersize will be total_xfersize or as much as possible ending on a page boundary.
2224			 */
2225			pageOffset = uio_offset(in_uio) & PAGE_MASK;
2226			xfersize = MIN(total_xfersize, ubc_upl_maxbufsize() - pageOffset);
2227
2228			/* create the upl so that we "own" the pages */
2229			kret = ubc_create_upl(vp,
2230				(off_t) trunc_page_64(uio_offset(in_uio)),
2231				(int) round_page_64(xfersize + pageOffset),
2232				&upl,
2233				&pl,
2234				UPL_FLAGS_NONE);
2235            if ( kret != KERN_SUCCESS )
2236			{
2237#if DEBUG
2238				printf("webdav_rdwr: ubc_create_upl failed %d\n", kret);
2239#endif
2240                error = EIO;
2241                goto unlock_exit;
2242            }
2243
2244			/* Scan pages looking for valid/invalid ranges of pages.
2245			 * uiomove() the ranges of valid pages; VOP_READ/WRITE the ranges of invalid pages.
2246			 */
2247			mapped_upl = FALSE;
2248			currentPage = 0;
2249			pagecount = atop_32(pageOffset + xfersize - 1) + 1;
2250			while ( currentPage < pagecount )
2251			{
2252				off_t firstPageOfRange;
2253				off_t lastPageOfRange;
2254				boolean_t rangeIsValid;
2255				off_t requestSize;
2256
2257				firstPageOfRange = lastPageOfRange = currentPage;
2258				rangeIsValid = upl_valid_page(pl, currentPage);
2259				++currentPage;
2260
2261				/* find last page with same state */
2262				while ( (currentPage < pagecount) && (upl_valid_page(pl, currentPage) == rangeIsValid) )
2263				{
2264					lastPageOfRange = currentPage;
2265					++currentPage;
2266				}
2267
2268				/* determine how much to uiomove() or VOP_READ() for this range of pages */
2269				requestSize = MIN(xfersize, (ptoa_32(lastPageOfRange - firstPageOfRange + 1) - pageOffset));
2270
2271				if ( rangeIsValid )
2272				{
2273					/* range is valid, uiomove it */
2274
2275					/* map the upl the first time we need it mapped */
2276					if ( !mapped_upl )
2277					{
2278						kret = ubc_upl_map(upl, &addr);
2279						if ( kret != KERN_SUCCESS )
2280						{
2281#if DEBUG
2282							printf("webdav_rdwr: ubc_upl_map failed %d\n", kret);
2283#endif
2284							error = EIO;
2285							goto unmap_unlock_exit;
2286						}
2287						mapped_upl = TRUE;
2288					}
2289
2290					/* uiomove the the range firstPageOfRange through firstPageOfRange */
2291					error = uiomove((void *)addr + ptoa_64(firstPageOfRange) + pageOffset,
2292						(int)requestSize,
2293						in_uio);
2294					if ( error )
2295					{
2296						goto unmap_unlock_exit;
2297					}
2298				}
2299				else
2300				{
2301					/* range is invalid, VOP_READ/WRITE it from the the cache file */
2302					user_ssize_t remainingRequestSize;
2303
2304					/* subtract requestSize from uio_resid and save */
2305					remainingRequestSize = uio_resid(in_uio) - requestSize;
2306
2307					/* adjust size of read */
2308					uio_setresid(in_uio, requestSize);
2309
2310					if ( reading )
2311					{
2312						/* read it from the cache file */
2313						error = VNOP_READ(cachevp, in_uio, 0 /* no flags */, ap->a_context);
2314					}
2315					else
2316					{
2317						/* write it to the cache file */
2318						error = VNOP_WRITE(cachevp, in_uio, 0 /* no flags */, ap->a_context);
2319					}
2320
2321					if ( error || (uio_resid(in_uio) != 0) )
2322					{
2323						uio_setresid(in_uio, remainingRequestSize + uio_resid(in_uio));
2324						goto unmap_unlock_exit;
2325					}
2326
2327					/* set remaining uio_resid */
2328					uio_setresid(in_uio, remainingRequestSize);
2329				}
2330
2331				if ( !reading )
2332				{
2333					/* after the write to the cache file has been completed, mark the file dirty */
2334					pt->pt_status |= WEBDAV_DIRTY;
2335					file_changed = TRUE;
2336				}
2337
2338				/* set pageOffset to zero (which it will be if we need to loop again)
2339				 * and decrement xfersize and total_xfersize by requestSize.
2340				 */
2341				pageOffset = 0;
2342				xfersize -= requestSize;
2343				total_xfersize -= requestSize;
2344			}
2345
2346			/* unmap the upl if needed */
2347			if ( mapped_upl )
2348			{
2349				kret = ubc_upl_unmap(upl);
2350				if (kret != KERN_SUCCESS)
2351				{
2352					panic("webdav_rdwr: ubc_upl_unmap() failed with (%d)", kret);
2353				}
2354			}
2355
2356			/* get rid of the upl */
2357			kret = ubc_upl_abort(upl, 0);
2358			if ( kret != KERN_SUCCESS )
2359			{
2360#if DEBUG
2361				printf("webdav_rdwr: ubc_upl_map failed %d\n", kret);
2362#endif
2363				error = EIO;
2364                goto unlock_exit;
2365			}
2366
2367		}	/* end while loop */
2368	}
2369	else
2370	{
2371		/* No UBC, or was never mapped */
2372		if ( reading )
2373		{
2374			/* pass the read along to the underlying cache file */
2375			error = VNOP_READ(cachevp, in_uio, ap->a_ioflag, ap->a_context);
2376		}
2377		else
2378		{
2379			/* pass the write along to the underlying cache file */
2380			error = VNOP_WRITE(cachevp, in_uio, ap->a_ioflag, ap->a_context);
2381
2382			/* after the write to the cache file has been completed... */
2383			pt->pt_status |= WEBDAV_DIRTY;
2384			file_changed = TRUE;
2385		}
2386	}
2387
2388unlock_exit:
2389
2390	if ( !reading )
2391	{
2392		/* Note: the sleep loop	at the top of this function ensures that the file can grow only
2393		 * if the file is completely downloaded.
2394		 */
2395		if ( file_changed &&							/* if the file changed */
2396			 (uio_offset(in_uio) > (off_t)attrbuf.va_data_size) )	/* and the file grew */
2397		{
2398			/* make sure the cache file's size is correct */
2399			struct vnode_attr vattr;
2400
2401			/* set the size of the cache file */
2402			VATTR_INIT(&vattr);
2403			VATTR_SET(&vattr, va_data_size, uio_offset(in_uio));
2404			error = vnode_setattr(cachevp, &vattr, ap->a_context);
2405
2406			/* let the UBC know the new size */
2407			if ( error == 0 )
2408			{
2409				(void) ubc_setsize(vp, uio_offset(in_uio)); /* ignore failures - nothing can be done */
2410			}
2411		}
2412	}
2413
2414exit:
2415
2416	return ( error );
2417
2418unmap_unlock_exit:
2419
2420	/* unmap the upl if it's mapped */
2421	if ( mapped_upl )
2422	{
2423		kret = ubc_upl_unmap(upl);
2424		if (kret != KERN_SUCCESS)
2425		{
2426			panic("webdav_rdwr: ubc_upl_unmap() failed with (%d)", kret);
2427		}
2428	}
2429
2430	/* get rid of the upl */
2431	kret = ubc_upl_abort(upl, UPL_ABORT_ERROR);
2432	if ( kret != KERN_SUCCESS )
2433	{
2434#if DEBUG
2435		printf("webdav_rdwr: ubc_upl_abort failed %d\n", kret);
2436#endif
2437		if (!error)
2438		{
2439			error = EIO;
2440		}
2441	}
2442
2443	goto unlock_exit;
2444}
2445
2446/*****************************************************************************/
2447
2448/*
2449 * webdav_vnop_read
2450 *
2451 * webdav_vnop_read calls webdav_rdwr to do the work.
2452 */
2453static int webdav_vnop_read(struct vnop_read_args *ap)
2454/*
2455	struct vnop_read_args {
2456		struct vnodeop_desc *a_desc;
2457		vnode_t a_vp;
2458		struct uio *a_uio;
2459		int a_ioflag;
2460		vfs_context_t a_context;
2461	};
2462*/
2463{
2464	struct webdavnode *pt;
2465	int error;
2466
2467	START_MARKER("webdav_vnop_read");
2468
2469	pt = VTOWEBDAV(ap->a_vp);
2470	webdav_lock(pt, WEBDAV_SHARED_LOCK);
2471	pt->pt_lastvop = webdav_vnop_read;
2472
2473	error = webdav_rdwr(ap);
2474	webdav_unlock(pt);
2475
2476	RET_ERR("webdav_vnop_read", error);
2477}
2478
2479/*****************************************************************************/
2480
2481/*
2482 * webdav_vnop_write
2483 *
2484 * webdav_vnop_write calls webdav_rdwr to do the work.
2485 */
2486static int webdav_vnop_write(struct vnop_write_args *ap)
2487/*
2488	struct vnop_write_args {
2489		struct vnodeop_desc *a_desc;
2490		vnode_t a_vp;
2491		struct uio *a_uio;
2492		int a_ioflag;
2493		vfs_context_t a_context;
2494	};
2495*/
2496{
2497	struct webdavnode *pt;
2498	int error;
2499	struct webdav_request_writeseq *req = NULL;
2500	struct webdav_reply_writeseq reply;
2501	vnode_t cachevp, vp;
2502	uio_t in_uio;
2503	struct vnode_attr vattr;
2504	struct webdavmount *fmp;
2505	int server_error;
2506
2507	START_MARKER("webdav_vnop_write");
2508	pt = VTOWEBDAV(ap->a_vp);
2509	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
2510
2511	vp = ap->a_vp;
2512	/* make sure this is not a directory */
2513	if ( vnode_isdir(vp) )
2514	{
2515		error = EISDIR;
2516		goto out1;
2517	}
2518
2519	if (!pt->pt_writeseq_enabled)
2520	{
2521		pt->pt_lastvop = webdav_vnop_write;
2522
2523		error = webdav_rdwr((struct vnop_read_args *)ap);
2524		webdav_unlock(pt);
2525		goto out2;
2526	}
2527
2528	/* write sequential mode */
2529	MALLOC(req, void *, sizeof(struct webdav_request_writeseq), M_TEMP, M_WAITOK);
2530	if (req == NULL) {
2531		error = ENOMEM;
2532		webdav_unlock(pt);
2533		goto out2;
2534	}
2535
2536	fmp = VFSTOWEBDAV(vnode_mount(vp));
2537	cachevp = pt->pt_cache_vnode;
2538	in_uio = ap->a_uio;
2539
2540#if DEBUG
2541	printf("KWRITESEQ: vnop_write: sequential write -> uio_offset: %llu uio_resid %llu\n",
2542		uio_offset(in_uio), (uint64_t)uio_resid(in_uio));
2543#endif
2544
2545	/* setup request before messing with uio */
2546	webdav_copy_creds(ap->a_context, &req->pcr);
2547	req->obj_id = pt->pt_obj_id;
2548	req->offset = uio_offset(in_uio);
2549	req->count = uio_resid(in_uio);
2550	req->file_len = pt->pt_writeseq_len;
2551
2552	/* reset uio to original state, before writing to the cache file */
2553
2554	/******************************************************/
2555	/*** Step 1. Write the data chunk to the cache file ***/
2556	/******************************************************/
2557
2558	/* make sure this is the offset we're expecting */
2559	if (pt->pt_writeseq_offset != uio_offset(in_uio))
2560	{
2561		printf("KWRITESEQ: vnop_write: wrong offset. uio_offset: %llu, expected: %llu\n",
2562			uio_offset(in_uio), pt->pt_writeseq_offset);
2563		error = EINVAL;
2564		goto out1;
2565	}
2566
2567	/* make sure there's a cache file vnode associated with the webdav vnode */
2568	if ( cachevp == NULLVP )
2569	{
2570		printf("KWRITESEQ: vnop_write: cache vnode is NULL\n");
2571		error = EIO;
2572		goto out1;
2573	}
2574
2575	/* pass the write along to the underlying cache file */
2576	error = VNOP_WRITE(cachevp, in_uio, 0, ap->a_context);
2577	if (error)
2578	{
2579		printf("KWRITESEQ: vnop_write: VNOP_WRITE failed, errno %d\n", error);
2580		goto out1;
2581	}
2582
2583	/* update the size of the cache file */
2584	VATTR_INIT(&vattr);
2585	VATTR_SET(&vattr, va_data_size, uio_offset(in_uio));
2586	error = vnode_setattr(cachevp, &vattr, ap->a_context);
2587
2588	if (error)
2589	{
2590		printf("KWRITESEQ: vnop_write: vnop_setattr failed, errno %d\n", error);
2591		goto out1;
2592	}
2593
2594	/* let the UBC know the new size */
2595	(void) ubc_setsize(vp, uio_offset(in_uio)); /* ignore failures - nothing can be done */
2596
2597	/*************************************************/
2598	/*** Step 2. Send the data chunk to the server ***/
2599	/*************************************************/
2600	error = webdav_sendmsg(WEBDAV_WRITESEQ, fmp,
2601			req, sizeof(struct webdav_request_writeseq),
2602			NULL, 0,
2603			&server_error, &reply, sizeof(struct webdav_reply_writeseq));
2604
2605	if ( (server_error == 0) && (error == 0) )
2606	{
2607		// update write sequential offset
2608		pt->pt_writeseq_offset += req->count;
2609#if DEBUG
2610		printf("KWRITESEQ: Success for offset:%llu len: %lu\n", req->offset, req->count);
2611#endif
2612		goto out1;
2613	} else {
2614		/* Otherwise, no error will be returned if server_error != EAGAIN even
2615		 * though we have an error at this point
2616		 */
2617		error = EIO;
2618	}
2619
2620	/*************************************************************/
2621	/*** EAGAIN,  Retry the PUT request one time from the start ***/
2622	/*************************************************************/
2623	if ( server_error == EAGAIN ) {
2624		req->is_retry = 1;
2625		req->offset = 0;
2626		req->count = pt->pt_writeseq_offset + req->count;
2627#if DEBUG
2628		printf("KWRITESEQ: Retrying PUT request of %lu bytes, server_error: %d error: %d\n", req->count, server_error, error);
2629#endif
2630		error = webdav_sendmsg(WEBDAV_WRITESEQ, fmp,
2631							   req, sizeof(struct webdav_request_writeseq),
2632							   NULL, 0,
2633							   &server_error, &reply, sizeof(struct webdav_reply_writeseq));
2634
2635		if ( (server_error == 0) && (error == 0))
2636		{
2637			// update write sequential offset
2638			pt->pt_writeseq_offset += req->count;
2639#if DEBUG
2640			printf("KWRITESEQ: Retry was successfull, count:%lu\n", req->count);
2641#endif
2642		} else {
2643			printf("KWRITESEQ: Retry failed, server_error %d, error %d\n", server_error, error);
2644			error = EIO;
2645		}
2646	}
2647
2648out1:
2649	webdav_unlock(pt);
2650	FREE((caddr_t)req, M_TEMP);
2651out2:
2652	RET_ERR("webdav_vnop_write", error);
2653}
2654
2655/*****************************************************************************/
2656
2657/*
2658 * webdav_vnop_getattr
2659 *
2660 * webdav_vnop_getattr returns the most up-to-date vattr information.
2661 *
2662 * results:
2663 *	0		Success.
2664 *	EIO		A physical I/O error has occurred, or this error was generated for
2665 *			implementation-defined reasons.
2666 */
2667static int webdav_vnop_getattr(struct vnop_getattr_args *ap)
2668/*
2669	struct vnop_getattr_args {
2670		struct vnodeop_desc *a_desc;
2671		vnode_t a_vp;
2672		struct vnode_attr *a_vap;
2673		vfs_context_t a_context;
2674	};
2675*/
2676{
2677	vnode_t vp;
2678	struct webdavnode *pt;
2679	int error;
2680
2681	START_MARKER("webdav_vnop_getattr");
2682
2683	vp = ap->a_vp;
2684	pt = VTOWEBDAV(vp);
2685
2686	webdav_lock(pt, WEBDAV_SHARED_LOCK);
2687	pt->pt_lastvop = webdav_vnop_getattr;
2688
2689	/* call the common routine */
2690	error = webdav_getattr_common(vp, ap->a_vap, ap->a_context);
2691
2692	webdav_unlock(pt);
2693
2694	RET_ERR("webdav_vnop_getattr", error);
2695}
2696
2697/*****************************************************************************/
2698
2699/*
2700 * webdav_vnop_remove
2701 *
2702 * webdav_vnop_remove removes a file.
2703 *
2704 * results:
2705 *	0		Success.
2706 *	EBUSY	Caller requested Carbon delete semantics and file was open.
2707 *	EIO		A physical I/O error has occurred, or this error was generated for
2708 *			implementation-defined reasons.
2709 */
2710static int webdav_vnop_remove(struct vnop_remove_args *ap)
2711/*
2712	struct vnop_remove_args {
2713		struct vnodeop_desc *a_desc;
2714		vnode_t a_dvp;
2715		vnode_t a_vp;
2716		struct componentname *a_cnp;
2717		int a_flags;
2718		vfs_context_t a_context;
2719	};
2720*/
2721{
2722	vnode_t vp;
2723	vnode_t dvp;
2724	struct webdavnode *pt;
2725	struct webdavmount *fmp;
2726	int error;
2727	int server_error;
2728	struct webdav_request_remove request_remove;
2729
2730	START_MARKER("webdav_vnop_remove");
2731
2732	vp = ap->a_vp;
2733	dvp = ap->a_dvp;
2734	fmp = VFSTOWEBDAV(vnode_mount(vp));
2735	pt = VTOWEBDAV(vp);
2736	error = server_error = 0;
2737
2738	/* lock parent node, then child */
2739	webdav_lock(VTOWEBDAV(dvp), WEBDAV_EXCLUSIVE_LOCK);
2740	if (pt != VTOWEBDAV(dvp))
2741		webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
2742	pt->pt_lastvop = webdav_vnop_remove;
2743	VTOWEBDAV(dvp)->pt_lastvop = webdav_vnop_remove;
2744
2745    if (vnode_isdir(vp))
2746    {
2747        error = EPERM;
2748        goto bad;
2749    }
2750
2751	/* If caller requested Carbon delete semantics and the file is in use, return EBUSY */
2752	if ( (ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0) )
2753	{
2754		error = EBUSY;
2755		goto bad;
2756	}
2757
2758	cache_purge(vp);
2759
2760	webdav_copy_creds(ap->a_context, &request_remove.pcr);
2761	request_remove.obj_id = pt->pt_obj_id;
2762
2763	error = webdav_sendmsg(WEBDAV_REMOVE, fmp,
2764		&request_remove, sizeof(struct webdav_request_remove),
2765		NULL, 0,
2766		&server_error, NULL, 0);
2767	if ( (error == 0) && (server_error != 0) )
2768	{
2769		if ( server_error == ESTALE )
2770		{
2771			/*
2772			 * The object id(s) passed to userland are invalid.
2773			 * Purge the vnode(s) and restart the request.
2774			 */
2775			webdav_purge_stale_vnode(vp);
2776			error = ERESTART;
2777		}
2778		else
2779		{
2780			error = server_error;
2781		}
2782	}
2783
2784	/*
2785	 *	When connected to a Mac OS X Server, I can delete the main file (ie blah.dmg), but when I try
2786	 *	to delete the ._ file (ie ._blah.dmg), I get a file not found error since the vfs layer on the
2787	 *	server already deleted the ._ file.  Since I am deleting the ._ anyways, the ENOENT is ok and I
2788	 *	should still clean up.
2789	 */
2790	if ((error) && (error != ENOENT))
2791	{
2792		goto bad;
2793	}
2794
2795	/* parent directory just changed, flush negative name cache entries */
2796	if (VTOWEBDAV(dvp)->pt_status & WEBDAV_NEGNCENTRIES)
2797	{
2798		VTOWEBDAV(dvp)->pt_status &= ~WEBDAV_NEGNCENTRIES;
2799		cache_purge_negatives(dvp);
2800	}
2801
2802	/* Get the node off of the cache so that other lookups
2803	 * won't find it and think the file still exists
2804	 */
2805	pt->pt_status |= WEBDAV_DELETED;
2806	(void) vnode_recycle(vp); /* we don't care if the recycle was done or not */
2807
2808bad:
2809
2810	if (!error) {
2811		/* if success, blow away statfs cache */
2812		fmp->pm_statfstime = 0;
2813	}
2814	/* unlock child node, then parent */
2815	webdav_unlock(pt);
2816	if (VTOWEBDAV(dvp) != pt)
2817		webdav_unlock(VTOWEBDAV(dvp));
2818
2819	RET_ERR("webdav_vnop_remove", error);
2820}
2821
2822/*****************************************************************************/
2823
2824/*
2825 * webdav_vnop_rmdir
2826 *
2827 * webdav_vnop_rmdir removes a directory.
2828 *
2829 * results:
2830 *	0		Success.
2831 *	ENOTEMPTY Directory was not empty.
2832 *	EIO		A physical I/O error has occurred, or this error was generated for
2833 *			implementation-defined reasons.
2834 */
2835static int webdav_vnop_rmdir(struct vnop_rmdir_args *ap)
2836/*
2837	struct vnop_rmdir_args {
2838		struct vnodeop_desc *a_desc;
2839		vnode_t a_dvp;
2840		vnode_t a_vp;
2841		struct componentname *a_cnp;
2842		vfs_context_t a_context;
2843	};
2844*/
2845{
2846	vnode_t vp;
2847	vnode_t dvp;
2848	struct webdavnode *pt;
2849	struct webdavmount *fmp;
2850	int error;
2851	int server_error;
2852	struct webdav_request_rmdir request_rmdir;
2853
2854	START_MARKER("webdav_vnop_rmdir");
2855
2856	vp = ap->a_vp;
2857	dvp = ap->a_dvp;
2858	pt = VTOWEBDAV(vp);
2859	fmp = VFSTOWEBDAV(vnode_mount(vp));
2860	error = server_error = 0;
2861
2862	/* lock parent node, then child */
2863	webdav_lock(VTOWEBDAV(dvp), WEBDAV_EXCLUSIVE_LOCK);
2864	if (pt != VTOWEBDAV(dvp))
2865		webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
2866	pt->pt_lastvop = webdav_vnop_rmdir;
2867	VTOWEBDAV(dvp)->pt_lastvop = webdav_vnop_rmdir;
2868
2869	/* No rmdir "." please. */
2870	if ( pt == VTOWEBDAV(dvp) )
2871	{
2872		error = EINVAL;
2873		goto bad;
2874	}
2875
2876	cache_purge(vp);
2877
2878	webdav_copy_creds(ap->a_context, &request_rmdir.pcr);
2879	request_rmdir.obj_id = pt->pt_obj_id;
2880
2881	error = webdav_sendmsg(WEBDAV_RMDIR, fmp,
2882		&request_rmdir, sizeof(struct webdav_request_rmdir),
2883		NULL, 0,
2884		&server_error, NULL, 0);
2885	if ( (error == 0) && (server_error != 0) )
2886	{
2887		if ( server_error == ESTALE )
2888		{
2889			/*
2890			 * The object id(s) passed to userland are invalid.
2891			 * Purge the vnode(s) and restart the request.
2892			 */
2893			webdav_purge_stale_vnode(vp);
2894			error = ERESTART;
2895		}
2896		else
2897		{
2898			error = server_error;
2899		}
2900	}
2901	if (error)
2902	{
2903		goto bad;
2904	}
2905
2906	/* parent directory just changed, flush negative name cache entries */
2907	if (VTOWEBDAV(dvp)->pt_status & WEBDAV_NEGNCENTRIES)
2908	{
2909		VTOWEBDAV(dvp)->pt_status &= ~WEBDAV_NEGNCENTRIES;
2910		cache_purge_negatives(dvp);
2911	}
2912
2913	/* Get the node off of the cache so that other lookups
2914	 * won't find it and think the file still exists
2915	 */
2916	pt->pt_status |= WEBDAV_DELETED;
2917	(void) vnode_recycle(vp); /* we don't care if the recycle was done or not */
2918
2919bad:
2920	/* if success, blow away statfs cache */
2921	if (!error) {
2922		fmp->pm_statfstime = 0;
2923	}
2924
2925	/* unlock child node, then parent */
2926	webdav_unlock(pt);
2927	if (VTOWEBDAV(dvp) != pt)
2928		webdav_unlock(VTOWEBDAV(dvp));
2929
2930	RET_ERR("webdav_vnop_rmdir", error);
2931}
2932
2933/*****************************************************************************/
2934
2935static int webdav_vnop_create(struct vnop_create_args *ap)
2936/*
2937	struct vnop_create_args {
2938		struct vnodeop_desc *a_desc;
2939		vnode_t a_dvp;
2940		vnode_t *a_vpp;
2941		struct componentname *a_cnp;
2942		struct vnode_attr *a_vap;
2943		vfs_context_t a_context;
2944	};
2945*/
2946{
2947	struct componentname *cnp = ap->a_cnp;
2948	vnode_t *vpp = ap->a_vpp;
2949	vnode_t dvp = ap->a_dvp;
2950	struct webdavmount *fmp;
2951	struct webdavnode *pt_dvp;
2952	int error = 0;
2953	int server_error = 0;
2954	struct timeval tv;
2955	struct timespec ts;
2956	struct webdav_request_create request_create;
2957	struct webdav_reply_create reply_create;
2958	struct webdav_timespec64 wts;
2959
2960	START_MARKER("webdav_vnop_create");
2961
2962	fmp = VFSTOWEBDAV(vnode_mount(dvp));
2963	pt_dvp =  VTOWEBDAV(dvp);
2964
2965	/* lock the parent webdavnode */
2966	webdav_lock(pt_dvp, WEBDAV_EXCLUSIVE_LOCK);
2967	pt_dvp->pt_lastvop = webdav_vnop_create;
2968
2969	/* Set *vpp to Null for error checking purposes */
2970	*vpp = NULL;
2971
2972	/* We don't support special files so make sure this is a regular file */
2973	if (ap->a_vap->va_type != VREG)
2974	{
2975		error = ENOTSUP;
2976		goto bad;
2977	}
2978
2979	webdav_copy_creds(ap->a_context, &request_create.pcr);
2980	request_create.dir_id = pt_dvp->pt_obj_id;
2981	request_create.mode = ap->a_vap->va_mode;
2982	request_create.name_length = cnp->cn_namelen;
2983
2984	bzero(&reply_create, sizeof(struct webdav_reply_create));
2985
2986	error = webdav_sendmsg(WEBDAV_CREATE, fmp,
2987		&request_create, offsetof(struct webdav_request_create, name),
2988		cnp->cn_nameptr, cnp->cn_namelen,
2989		&server_error, &reply_create, sizeof(struct webdav_reply_create));
2990	if ( (error == 0) && (server_error != 0) )
2991	{
2992		if ( server_error == ESTALE )
2993		{
2994			/*
2995			 * The object id(s) passed to userland are invalid.
2996			 * Purge the vnode(s) and restart the request.
2997			 */
2998			webdav_purge_stale_vnode(dvp);
2999			error = ERESTART;
3000		}
3001		else
3002		{
3003			error = server_error;
3004		}
3005	}
3006	if (error)
3007	{
3008		goto bad;
3009	}
3010
3011	/* parent directory changed so force readdir to reload */
3012	VTOWEBDAV(dvp)->pt_status |= WEBDAV_DIR_NOT_LOADED;
3013
3014	/* parent directory just changed, flush negative name cache entries */
3015	if (VTOWEBDAV(dvp)->pt_status & WEBDAV_NEGNCENTRIES)
3016	{
3017		VTOWEBDAV(dvp)->pt_status &= ~WEBDAV_NEGNCENTRIES;
3018		cache_purge_negatives(dvp);
3019	}
3020
3021	microtime(&tv);
3022	TIMEVAL_TO_TIMESPEC(&tv, &ts);
3023
3024	// convert ts to webdav_timespec64
3025	timespec_to_webdav_timespec64(ts, &wts);
3026	error = webdav_get(vnode_mount(dvp), dvp, 0, cnp,
3027		reply_create.obj_id, reply_create.obj_fileid, VREG, wts, wts, wts, wts, 0, vpp);
3028	if (error)
3029	{
3030		/* nothing we can do except complain */
3031		printf("webdav_vnop_create: webdav_get failed\n");
3032	}
3033	else
3034		webdav_unlock(VTOWEBDAV(*vpp));
3035
3036bad:
3037
3038	/* if success, blow away statfs cache */
3039	if (!error)
3040		fmp->pm_statfstime = 0;
3041
3042	webdav_unlock(pt_dvp);
3043
3044	RET_ERR("webdav_vnop_create", error);
3045}
3046
3047/*****************************************************************************/
3048
3049static int webdav_vnop_rename(struct vnop_rename_args *ap)
3050/*
3051	struct vnop_rename_args {
3052		struct vnodeop_desc *a_desc;
3053		vnode_t a_fdvp;
3054		vnode_t a_fvp;
3055		struct componentname *a_fcnp;
3056		vnode_t a_tdvp;
3057		vnode_t a_tvp;
3058		struct componentname *a_tcnp;
3059		vfs_context_t a_context;
3060	};
3061*/
3062{
3063	vnode_t fvp = ap->a_fvp;
3064	vnode_t tvp = ap->a_tvp;
3065	vnode_t tdvp = ap->a_tdvp;
3066	vnode_t fdvp = ap->a_fdvp;
3067	struct componentname *tcnp = ap->a_tcnp;
3068	struct webdavnode *fdpt;
3069	struct webdavnode *fpt;
3070	struct webdavnode *tdpt;
3071	struct webdavnode *tpt;
3072	struct webdavmount *wmp = VFSTOWEBDAV(vnode_mount(fvp));
3073	struct webdavnode *lock_order[4] = {NULL};
3074	int lock_cnt = 0;
3075	int ii;
3076	int vtype;
3077	int error = 0;
3078	int server_error = 0;
3079	struct webdav_request_rename request_rename;
3080
3081	START_MARKER("webdav_vnop_rename");
3082
3083
3084	/* Check for cross-device rename */
3085	if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
3086		(tvp && (vnode_mount(fvp) != vnode_mount(tvp))))
3087		return (EXDEV);
3088
3089	vtype = vnode_vtype(fvp);
3090	if ( (vtype != VDIR) && (vtype != VREG) && (vtype != VLNK) )
3091		return (EINVAL);
3092
3093	fdpt = VTOWEBDAV(fdvp);
3094	fpt = VTOWEBDAV(fvp);
3095	tdpt = VTOWEBDAV(tdvp);
3096	if (tvp)
3097		tpt = VTOWEBDAV(tvp);
3098	else
3099		tpt = NULL;
3100
3101	/*
3102	 * First lets deal with the parents. If they are the same only lock the from
3103	 * vnode, otherwise see if one is the parent of the other. We always want
3104	 * to lock in parent child order if we can. If they are not the parent of
3105	 * each other then lock in address order.
3106	 */
3107	lck_mtx_lock(&wmp->pm_renamelock);
3108	if (fdvp == tdvp)
3109		lock_order[lock_cnt++] = fdpt;
3110	else if (fdpt->pt_parent  && (fdpt->pt_parent == tdvp)) {
3111		lock_order[lock_cnt++] = tdpt;
3112		lock_order[lock_cnt++] = fdpt;
3113	} else if (tdpt->pt_parent && (tdpt->pt_parent == fdvp)) {
3114		lock_order[lock_cnt++] = fdpt;
3115		lock_order[lock_cnt++] = tdpt;
3116	} else if (fdpt < tdpt) {
3117		lock_order[lock_cnt++] = fdpt;
3118		lock_order[lock_cnt++] = tdpt;
3119	} else {
3120		lock_order[lock_cnt++] = tdpt;
3121		lock_order[lock_cnt++] = fdpt;
3122	}
3123	/*
3124	 * Now lets deal with the children. If any of the following is true then just
3125	 * lock the from vnode:
3126	 *		1. The to vnode doesn't exist
3127	 *		2. The to vnode and from vnodes are the same
3128	 *		3. The to vnode and the from parent vnodes are the same, I know
3129	 *		   it's strange but can happen.
3130	 * Otherwise, lock in address order
3131	 */
3132	if ((tvp == NULL) || (tvp == fvp) || (tvp == fdvp))
3133		lock_order[lock_cnt++] = fpt;
3134	else {
3135		if (fpt < tpt) {
3136			lock_order[lock_cnt++] = fpt;
3137			lock_order[lock_cnt++] = tpt;
3138		} else {
3139			lock_order[lock_cnt++] = tpt;
3140			lock_order[lock_cnt++] = fpt;
3141		}
3142	}
3143
3144
3145	lck_mtx_unlock(&wmp->pm_renamelock);
3146
3147	for (ii = 0; ii < lock_cnt; ii++)
3148	{
3149		if (lock_order[ii])
3150		{
3151			webdav_lock(lock_order[ii], WEBDAV_EXCLUSIVE_LOCK);
3152			lock_order[ii]->pt_lastvop = webdav_vnop_rename;
3153		}
3154	}
3155
3156	cache_purge(fvp);
3157
3158	webdav_copy_creds(ap->a_context, &request_rename.pcr);
3159	request_rename.from_dir_id = VTOWEBDAV(fdvp)->pt_obj_id;
3160	request_rename.from_obj_id = VTOWEBDAV(fvp)->pt_obj_id;
3161	request_rename.to_dir_id = VTOWEBDAV(tdvp)->pt_obj_id;
3162	request_rename.to_obj_id = (tvp != NULLVP) ? VTOWEBDAV(tvp)->pt_obj_id : 0;
3163	request_rename.to_name_length = tcnp->cn_namelen;
3164
3165	error = webdav_sendmsg(WEBDAV_RENAME, VFSTOWEBDAV(vnode_mount(fvp)),
3166		&request_rename, offsetof(struct webdav_request_rename, to_name),
3167		tcnp->cn_nameptr, tcnp->cn_namelen,
3168		&server_error, NULL, 0);
3169
3170	if ( (error == 0) && (server_error != 0) )
3171	{
3172		if ( server_error == ESTALE )
3173		{
3174			/*
3175			 * The object id(s) passed to userland are invalid.
3176			 * Purge the vnode(s) and restart the request.
3177			 */
3178			webdav_purge_stale_vnode(fdvp);
3179			webdav_purge_stale_vnode(fvp);
3180			webdav_purge_stale_vnode(tdvp);
3181			if ( tvp != NULLVP )
3182			{
3183				webdav_purge_stale_vnode(tvp);
3184			}
3185			error = ERESTART;
3186			goto done;
3187		}
3188		else
3189		{
3190			error = server_error;
3191		}
3192	}
3193
3194	if ( tvp != NULLVP )
3195	{
3196		/* tvp may have been deleted even if the move failed so get it out of the cache */
3197		if (tvp != fvp)
3198		{
3199			cache_purge(tvp);
3200		}
3201
3202		/* if no errors, we know tvp was deleted */
3203		if ( error == 0 )
3204		{
3205			VTOWEBDAV(tvp)->pt_status |= WEBDAV_DELETED;
3206			(void) vnode_recycle(tvp); /* we don't care if the recycle was done or not */
3207		}
3208	}
3209
3210	/* parent directories may have changed (even if error) so force readdir to reload */
3211	VTOWEBDAV(fdvp)->pt_status |= WEBDAV_DIR_NOT_LOADED;
3212	VTOWEBDAV(tdvp)->pt_status |= WEBDAV_DIR_NOT_LOADED;
3213
3214	/* if success, blow away statfs cache */
3215	if (!error)
3216		wmp->pm_statfstime = 0;
3217
3218	/* Purge negative cache entries in the destination directory */
3219	if (VTOWEBDAV(tdvp)->pt_status & WEBDAV_NEGNCENTRIES)
3220	{
3221		VTOWEBDAV(tdvp)->pt_status &= ~WEBDAV_NEGNCENTRIES;
3222		cache_purge_negatives(tdvp);
3223	}
3224
3225done:
3226	/* Unlock all nodes in reverse order */
3227	for ( ii = lock_cnt - 1; ii >= 0; ii--)
3228	{
3229		if (lock_order[ii])
3230			webdav_unlock(lock_order[ii]);
3231	}
3232
3233	RET_ERR("webdav_vnop_rename", error);
3234}
3235
3236/*****************************************************************************/
3237
3238static int webdav_vnop_mkdir(struct vnop_mkdir_args *ap)
3239/*
3240	struct vnop_mkdir_args {
3241		struct vnodeop_desc *a_desc;
3242		vnode_t a_dvp;
3243		vnode_t *a_vpp;
3244		struct componentname *a_cnp;
3245		struct vnode_attr *a_vap;
3246		vfs_context_t a_context;
3247	};
3248*/
3249{
3250	struct componentname *cnp = ap->a_cnp;
3251	vnode_t *vpp = ap->a_vpp;
3252	vnode_t dvp = ap->a_dvp;
3253	struct webdavmount *fmp;
3254	struct webdavnode *pt_dvp;
3255	int error = 0;
3256	int server_error = 0;
3257	struct timeval tv;
3258	struct timespec ts;
3259	struct webdav_request_mkdir request_mkdir;
3260	struct webdav_reply_mkdir reply_mkdir;
3261	struct webdav_timespec64 wts;
3262
3263	START_MARKER("webdav_vnop_mkdir");
3264
3265	fmp = VFSTOWEBDAV(vnode_mount(dvp));
3266	pt_dvp =  VTOWEBDAV(dvp);
3267
3268	/* lock parent vnode */
3269	webdav_lock(pt_dvp, WEBDAV_EXCLUSIVE_LOCK);
3270	pt_dvp->pt_lastvop = webdav_vnop_mkdir;
3271
3272	/* Set *vpp to Null for error checking purposes */
3273	*vpp = NULL;
3274
3275	/* Make sure this is a directory */
3276	if (ap->a_vap->va_type != VDIR)
3277	{
3278		error = ENOTDIR;
3279		goto bad;
3280	}
3281
3282	webdav_copy_creds(ap->a_context, &request_mkdir.pcr);
3283	request_mkdir.dir_id = VTOWEBDAV(dvp)->pt_obj_id;
3284	request_mkdir.mode = ap->a_vap->va_mode;
3285	request_mkdir.name_length = cnp->cn_namelen;
3286
3287	bzero(&reply_mkdir, sizeof(struct webdav_reply_mkdir));
3288
3289	error = webdav_sendmsg(WEBDAV_MKDIR, fmp,
3290		&request_mkdir, offsetof(struct webdav_request_mkdir, name),
3291		cnp->cn_nameptr, cnp->cn_namelen,
3292		&server_error, &reply_mkdir, sizeof(struct webdav_reply_mkdir));
3293	if ( (error == 0) && (server_error != 0) )
3294	{
3295		if ( server_error == ESTALE )
3296		{
3297			/*
3298			 * The object id(s) passed to userland are invalid.
3299			 * Purge the vnode(s) and restart the request.
3300			 */
3301			webdav_purge_stale_vnode(dvp);
3302			error = ERESTART;
3303		}
3304		else
3305		{
3306			error = server_error;
3307		}
3308	}
3309	if (error)
3310	{
3311		goto bad;
3312	}
3313
3314	/* parent directory changed so force readdir to reload */
3315	VTOWEBDAV(dvp)->pt_status |= WEBDAV_DIR_NOT_LOADED;
3316
3317	/* parent directory changed so flush negative name cache */
3318	if (pt_dvp->pt_status & WEBDAV_NEGNCENTRIES)
3319	{
3320		pt_dvp->pt_status &= ~WEBDAV_NEGNCENTRIES;
3321		cache_purge_negatives(dvp);
3322	}
3323
3324	microtime(&tv);
3325	TIMEVAL_TO_TIMESPEC(&tv, &ts);
3326
3327	// webdav_get wants a webdav_timespec64
3328	timespec_to_webdav_timespec64(ts, &wts);
3329	error = webdav_get(vnode_mount(dvp), dvp, 0, cnp,
3330		reply_mkdir.obj_id, reply_mkdir.obj_fileid, VDIR, wts, wts, wts, wts, fmp->pm_dir_size, vpp);
3331	if (error)
3332	{
3333		/* nothing we can do except complain */
3334		printf("webdav_vnop_mkdir: webdav_get failed\n");
3335	}
3336	else
3337	{
3338		/* webdav_get() returns the node locked */
3339		webdav_unlock(VTOWEBDAV(*vpp));
3340	}
3341
3342bad:
3343
3344	/* if success, blow away statfs cache */
3345	if(!error)
3346		fmp->pm_statfstime = 0;
3347
3348	webdav_unlock(pt_dvp);
3349
3350	RET_ERR("webdav_vnop_mkdir", error);
3351}
3352
3353/*****************************************************************************/
3354
3355/*
3356 * webdav_vnop_setattr
3357 *
3358 * webdav_vnop_setattr set the attributes of a file.
3359 *
3360 * results:
3361 *	0		Success.
3362 *	EACCES	vp was VROOT.
3363 *	EINVAL	unsettable attribute
3364 *	EROFS	read-only file system
3365 *	EIO		A physical I/O error has occurred, or this error was generated for
3366 *			implementation-defined reasons.
3367 */
3368static int webdav_vnop_setattr(struct vnop_setattr_args *ap)
3369/*
3370	struct vnop_setattr_args {
3371		struct vnodeop_desc *a_desc;
3372		vnode_t a_vp;
3373		struct vnode_attr *a_vap;
3374		vfs_context_t a_context;
3375	};
3376*/
3377{
3378	int error;
3379	vnode_t vp;
3380	struct webdavnode *pt;
3381	vnode_t cachevp;
3382	struct vnode_attr attrbuf;
3383	struct vnop_open_args open_ap;
3384	struct vnop_close_args close_ap;
3385	boolean_t is_open;
3386
3387	START_MARKER("webdav_vnop_setattr");
3388
3389	error = 0;
3390	is_open = FALSE;
3391	vp = ap->a_vp;
3392	pt = VTOWEBDAV(vp);
3393
3394	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
3395	pt->pt_lastvop = webdav_vnop_setattr;
3396
3397	/* Can't mess with the root vnode */
3398	if (vnode_isvroot(vp))
3399	{
3400		error = EACCES;
3401		goto exit;
3402	}
3403
3404	// Is the file is not opened (no cache file), then
3405	// temporarily open it
3406	if (pt->pt_cache_vnode == NULLVP)
3407	{
3408		open_ap.a_desc = &vnop_open_desc;
3409		open_ap.a_vp = ap->a_vp;
3410		open_ap.a_mode = FWRITE;
3411		open_ap.a_context = ap->a_context;
3412
3413		error = webdav_vnop_open_locked(&open_ap);
3414		if (!error)
3415			is_open = TRUE;
3416	}
3417
3418	/* If there is a local cache file, we'll allow setting.	We won't talk to the
3419	 * server, but we will honor the local file set. This will at least make fsx work.
3420	 */
3421	cachevp = pt->pt_cache_vnode;
3422	if ( cachevp != NULLVP )
3423	{
3424		/*
3425		 * Don't set UF_NODUMP or UF_APPEND on the cache file as they are used as
3426		 * flags by the agent.
3427		 */
3428
3429		ap->a_vap->va_flags &= ~(UF_NODUMP | UF_APPEND);
3430
3431		/* If we are changing the size, call ubc_setsize to fix things up
3432		 * with the UBC Also, make sure that we wait until the file is
3433		 * completely downloaded */
3434		if (VATTR_IS_ACTIVE(ap->a_vap, va_data_size) && vnode_isreg(vp))
3435		{
3436			do
3437			{
3438				VATTR_INIT(&attrbuf);
3439				VATTR_WANTED(&attrbuf, va_flags);
3440				VATTR_WANTED(&attrbuf, va_data_size);
3441				error = vnode_getattr(cachevp, &attrbuf, ap->a_context);
3442				if (error)
3443				{
3444					goto exit;
3445				}
3446
3447				if (attrbuf.va_flags & UF_NODUMP)
3448				{
3449					struct timespec ts;
3450
3451					/* We are downloading the file and we haven't finished
3452					* since the user process is going to extend the file with
3453					* writes until it is done, so sleep, and then check again.
3454					*/
3455					ts.tv_sec = 0;
3456					ts.tv_nsec = WEBDAV_WAIT_FOR_PAGE_TIME;
3457					error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_vnop_setattr", &ts);
3458					if ( error)
3459					{
3460						if ( error == EWOULDBLOCK )
3461						{
3462							error = 0;
3463						}
3464						else
3465						{
3466							printf("webdav_vnop_setattr: msleep(): %d\n", error);
3467							/* convert pseudo-errors to EIO */
3468							if ( error < 0 )
3469							{
3470								error = EIO;
3471							}
3472							goto exit;
3473						}
3474					}
3475				}
3476				else
3477				{
3478					/* the file has been downloaded */
3479					break; /* out of while (TRUE) loop */
3480				}
3481			} while ( TRUE );
3482
3483			if (attrbuf.va_flags & UF_APPEND)
3484			{
3485				/* If the UF_APPEND flag is set, there was an error downloading the file from the
3486				 * server, so exit with an EIO result.
3487				 */
3488				error = EIO;
3489				goto exit;
3490			}
3491
3492			/* At this point, cachevp is locked and the file is completely downloaded into cachevp */
3493
3494			/* If the size of the file is changed, set WEBDAV_DIRTY.
3495			 * WEBDAV_DIRTY is not set for other cases of setattr
3496			 * because we don't actually save va_flags, va_mode,
3497			 * va_uid, va_gid, va_atime or va_mtime on the server.
3498			 *
3499			 * XXX -- Eventually, we need to add code to call
3500			 * webdav_lock() in webdav_file.c to make sure we have
3501			 * a LOCK on the WebDAV server, so we don't try to PUT
3502			 * with no lock. The way things work now, the truncate
3503			 * might not stick if the file on the server isn't open
3504			 * with write access and some other client of the server
3505			 * has a WebDAV LOCK on the file (in which case fsync will
3506			 * fail with EBUSY when it tries to PUT the file to the
3507			 * server).
3508			 */
3509			if ( ap->a_vap->va_data_size != attrbuf.va_data_size || (off_t)ap->a_vap->va_data_size != pt->pt_filesize )
3510			{
3511				pt->pt_status |= WEBDAV_DIRTY;
3512			}
3513
3514			/* set the size and other attributes of the cache file */
3515			error = vnode_setattr(cachevp, ap->a_vap, ap->a_context);
3516
3517			/* let the UBC know the new size */
3518			(void) ubc_setsize(vp, (off_t)ap->a_vap->va_data_size); /* ignore failures - nothing can be done */
3519			pt->pt_filesize = (off_t)ap->a_vap->va_data_size;
3520		}
3521		else
3522		{
3523			/* set the attributes of the cache file */
3524			error = vnode_setattr(cachevp, ap->a_vap, ap->a_context);
3525		}
3526	}
3527
3528exit:
3529	if (is_open == TRUE) {
3530		close_ap.a_desc = &vnop_close_desc;
3531		close_ap.a_vp = ap->a_vp;
3532		close_ap.a_fflag = FWRITE;
3533		close_ap.a_context = ap->a_context;
3534		webdav_vnop_close_locked(&close_ap);
3535	}
3536	webdav_unlock(pt);
3537
3538	RET_ERR("webdav_vnop_setattr", error);
3539}
3540
3541/*****************************************************************************/
3542
3543/*
3544 * webdav_vnop_readdir
3545 *
3546 * webdav_vnop_readdir reads directory entries. We'll use the cache file for
3547 * the needed I/O.
3548 *
3549 * results:
3550 *	0		Success.
3551 *	ENOTDIR	attempt to use non-VDIR.
3552 *	EINVAL	no cache file, or attempt to read from illegal offset in the directory.
3553 *	EIO		A physical I/O error has occurred, or this error was generated for
3554 *			implementation-defined reasons.
3555 */
3556static int webdav_vnop_readdir(struct vnop_readdir_args *ap)
3557/*
3558	struct vnop_readdir_args {
3559		struct vnodeop_desc *a_desc;
3560		vnode_t a_vp;
3561		struct uio *a_uio;
3562		int a_flags;
3563		int *a_eofflag;
3564		int *a_numdirent;
3565		vfs_context_t a_context;
3566	};
3567*/
3568{
3569	vnode_t vp;
3570	vnode_t cachevp;
3571	struct webdavnode *pt;
3572	struct webdavmount *fmp;
3573	int server_error;
3574	uio_t uio;
3575	int error;
3576	user_ssize_t count, lost;
3577	struct vnode_attr vattr;
3578
3579	START_MARKER("webdav_vnop_readdir");
3580
3581	vp = ap->a_vp;
3582	uio = ap->a_uio;
3583	pt = VTOWEBDAV(vp);
3584	fmp = VFSTOWEBDAV(vnode_mount(vp));
3585	error = 0;
3586
3587	webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
3588	pt->pt_lastvop = webdav_vnop_readdir;
3589
3590	/* First make sure it is a directory we are dealing with */
3591	if ( !vnode_isdir(vp))
3592	{
3593		error = ENOTDIR;
3594		goto done;
3595	}
3596
3597	/* Make sure we have a cache file. If not the call must be wrong some how */
3598	cachevp = pt->pt_cache_vnode;
3599	if ( cachevp == NULLVP )
3600	{
3601		error = EINVAL;
3602		goto done;
3603	}
3604
3605	/* No support for requires seek offset (cookies) */
3606	if ( ap->a_flags & VNODE_READDIR_REQSEEKOFF )
3607	{
3608		error = EINVAL;
3609		goto done;
3610	}
3611
3612	/*
3613	 * If we starting from the beginning or WEBDAV_DIR_NOT_LOADED is set,
3614	 * refresh the directory with the latest from the server.
3615	 */
3616	if ( (uio_offset(uio) == 0) || (pt->pt_status & WEBDAV_DIR_NOT_LOADED) )
3617	{
3618		struct webdav_request_readdir request_readdir;
3619
3620		webdav_copy_creds(ap->a_context, &request_readdir.pcr);
3621		request_readdir.obj_id = pt->pt_obj_id;
3622		request_readdir.cache = !vnode_isnocache(vp);
3623
3624		error = webdav_sendmsg(WEBDAV_READDIR, fmp,
3625			&request_readdir, sizeof(struct webdav_request_readdir),
3626			NULL, 0,
3627			&server_error, NULL, 0);
3628		if ( (error == 0) && (server_error != 0) )
3629		{
3630			if ( server_error == ESTALE )
3631			{
3632				/*
3633				 * The object id(s) passed to userland are invalid.
3634				 * Purge the vnode(s) and restart the request.
3635				 */
3636				webdav_purge_stale_vnode(vp);
3637				error = ERESTART;
3638			}
3639			else
3640			{
3641				error = server_error;
3642			}
3643		}
3644		if (error)
3645		{
3646			/* set the WEBDAV_DIR_NOT_LOADED flag */
3647			pt->pt_status |= WEBDAV_DIR_NOT_LOADED;
3648			goto done;
3649		}
3650
3651		/* We didn't get an error so clear the WEBDAV_DIR_NOT_LOADED flag */
3652		pt->pt_status &= ~WEBDAV_DIR_NOT_LOADED;
3653	}
3654
3655	if ( ap->a_flags & VNODE_READDIR_EXTENDED )
3656	{
3657		/* XXX No support for VNODE_READDIR_EXTENDED (yet) */
3658		error = EINVAL;
3659	}
3660	else
3661	{
3662		/* Make sure we don't return partial entries. */
3663		if ( ((uio_offset(uio) % sizeof(struct dirent)) != 0) ||
3664			 (uio_resid(uio) < (user_ssize_t)sizeof(struct dirent)) )
3665		{
3666			error = EINVAL;
3667			goto done;
3668		}
3669
3670		count = uio_resid(uio);
3671		count -= (uio_offset(uio) + count) % sizeof(struct dirent);
3672		if (count <= 0)
3673		{
3674			error = EINVAL;
3675			goto done;
3676		}
3677
3678		lost = uio_resid(uio) - count;
3679		uio_setresid(uio, count);
3680
3681		error = VNOP_READ(cachevp, uio, 0, ap->a_context);
3682
3683		uio_setresid(uio, uio_resid(uio) + lost);
3684
3685		if (ap->a_eofflag)
3686		{
3687			VATTR_INIT(&vattr);
3688			VATTR_WANTED(&vattr, va_data_size);
3689			error = vnode_getattr(cachevp, &vattr, ap->a_context);
3690			if (error)
3691			{
3692				goto done;
3693			}
3694			*ap->a_eofflag = (off_t)vattr.va_data_size <= uio_offset(uio);
3695		}
3696	}
3697
3698done:
3699
3700	webdav_unlock(pt);
3701
3702	RET_ERR("webdav_vnop_readdir", error);
3703}
3704
3705/*****************************************************************************/
3706
3707/*
3708 * webdav_vnop_inactive
3709 *
3710 * Note: A message cannot be sent to user-land from here because it might cause
3711 * a recursive problem (connecting needs a vnode, which could bring control back
3712 * here again). So, all communication with user-land is done at close or mnomap.
3713 */
3714static int webdav_vnop_inactive(struct vnop_inactive_args *ap)
3715/*
3716	struct vnop_inactive_args {
3717		struct vnodeop_desc *a_desc;
3718		vnode_t a_vp;
3719		vfs_context_t a_context;
3720	};
3721*/
3722{
3723	#pragma unused(ap)
3724	START_MARKER("webdav_vnop_inactive");
3725
3726	RET_ERR("webdav_vnop_inactive", 0);
3727}
3728
3729/*****************************************************************************/
3730
3731static int webdav_vnop_reclaim(struct vnop_reclaim_args *ap)
3732/*
3733	struct vnop_reclaim_args {
3734		struct vnodeop_desc *a_desc;
3735		vnode_t a_vp;
3736		vfs_context_t a_context;
3737	};
3738*/
3739{
3740	vnode_t vp;
3741	struct webdavnode *pt;
3742
3743	START_MARKER("webdav_vnop_reclaim");
3744
3745	vp = ap->a_vp;
3746	pt = VTOWEBDAV(vp);
3747
3748	/* remove the reference added for pt_vnode */
3749	vnode_removefsref(vp);
3750
3751	/* remove from hash */
3752	webdav_hashrem(VTOWEBDAV(vp));
3753
3754	/*
3755	 * In case we block during FREE_ZONEs below, get the entry out
3756	 * of the name cache now so subsequent lookups won't find it.
3757	 */
3758	cache_purge(vp);
3759
3760	/* remove webdavnode pointer from vnode */
3761	vnode_clearfsnode(vp);
3762
3763	/* free the webdavnode */
3764	lck_rw_destroy(&pt->pt_rwlock, webdav_rwlock_group);
3765	FREE(pt, M_TEMP);
3766
3767	RET_ERR("webdav_vnop_reclaim", 0);
3768}
3769
3770/*****************************************************************************/
3771
3772/*
3773 * webdav_vnop_pathconf
3774 *
3775 * Return POSIX pathconf information.
3776 */
3777static int webdav_vnop_pathconf(struct vnop_pathconf_args *ap)
3778/*
3779	struct vnop_pathconf_args {
3780		struct vnodeop_desc *a_desc;
3781		vnode_t a_vp;
3782		int a_name;
3783		register_t *a_retval;
3784		vfs_context_t a_context;
3785	};
3786*/
3787{
3788	int error;
3789	struct webdavmount *fmp;
3790
3791	START_MARKER("webdav_vnop_pathconf");
3792
3793	fmp = VFSTOWEBDAV(vnode_mount(ap->a_vp));
3794
3795	/* default to return if a name argument is not supported or value is not provided */
3796	*ap->a_retval = -1;
3797	error = EINVAL;
3798
3799	switch (ap->a_name)
3800	{
3801		case _PC_LINK_MAX:
3802			/* maximum value of a file's link count (1 for file systems that do not support link counts) */
3803			if ( fmp->pm_link_max >= 0 )
3804			{
3805				*ap->a_retval = fmp->pm_link_max;
3806				error = 0;
3807			}
3808			break;
3809
3810		case _PC_NAME_MAX:
3811			/* The maximum number of bytes in a file name (does not include null at end) */
3812			if ( fmp->pm_name_max >= 0 )
3813			{
3814				*ap->a_retval = fmp->pm_name_max;
3815				error = 0;
3816			}
3817			break;
3818
3819		case _PC_PATH_MAX:
3820			/* The maximum number of bytes in a relative pathname (does not include null at end) */
3821			if ( fmp->pm_path_max >= 0 )
3822			{
3823				*ap->a_retval = fmp->pm_path_max;
3824				error = 0;
3825			}
3826			break;
3827
3828		case _PC_PIPE_BUF:
3829			/* The maximum number of bytes that can be written atomically to a pipe (usually PIPE_BUF if supported) */
3830			if ( fmp->pm_pipe_buf >= 0 )
3831			{
3832				*ap->a_retval = fmp->pm_pipe_buf;
3833				error = 0;
3834			}
3835			break;
3836
3837		case _PC_CHOWN_RESTRICTED:
3838			/* Return _POSIX_CHOWN_RESTRICTED if appropriate privileges are required for the chown(2) */
3839			if ( fmp->pm_chown_restricted >= 0 )
3840			{
3841				*ap->a_retval = fmp->pm_chown_restricted;
3842				error = 0;
3843			}
3844			break;
3845
3846		case _PC_NO_TRUNC:
3847			/* Return _POSIX_NO_TRUNC if file names longer than KERN_NAME_MAX are truncated */
3848			if ( fmp->pm_no_trunc >= 0 )
3849			{
3850				*ap->a_retval = fmp->pm_no_trunc;
3851				error = 0;
3852			}
3853			break;
3854
3855		default:
3856			/* other name arguments are not supported */
3857			break;
3858	}
3859
3860	RET_ERR("webdav_vnop_pathconf", error);
3861}
3862
3863/*****************************************************************************/
3864
3865/*
3866 * webdav_vnop_pagein
3867 *
3868 * Page in (read) a page of a file into memory.
3869 *
3870 * Note:
3871 *		To aovid deadlock, the webdavnode lock is not taken here. The lock is already held
3872 *		by the originating webdav vnop (calling into the ubc).
3873 *
3874 * To do:
3875 *		If UBC ever supports an efficient way to move pages from the cache file's
3876 *		UPL to the webdav UPL (a_pl), use that method instead of calling VOP_READ.
3877 */
3878static int webdav_vnop_pagein(struct vnop_pagein_args *ap)
3879/*
3880	struct vnop_pagein_args {
3881		struct vnodeop_desc *a_desc;
3882		vnode_t a_vp;
3883		upl_t a_pl;
3884		vm_offset_t a_pl_offset;
3885		off_t a_f_offset;
3886		size_t a_size;
3887		int a_flags;
3888		vfs_context_t a_context;
3889	};
3890*/
3891{
3892	vm_offset_t ioaddr;
3893	uio_t auio;
3894	vnode_t cachevp;
3895	vnode_t vp;
3896	struct webdavnode *pt;
3897	off_t bytes_to_zero;
3898	int error;
3899	int tried_bytes;
3900	struct vnode_attr attrbuf;
3901	kern_return_t kret;
3902
3903	START_MARKER("webdav_vnop_pagein");
3904
3905	vp = ap->a_vp;
3906	pt = VTOWEBDAV(vp);
3907	error = 0;
3908	cachevp = pt->pt_cache_vnode;
3909
3910	auio = uio_create(1, ap->a_f_offset, UIO_SYSSPACE, UIO_READ);
3911	if ( auio == NULL )
3912	{
3913		error = EIO;
3914		goto exit_no_unmap;
3915	}
3916
3917	kret = ubc_upl_map(ap->a_pl, &ioaddr);
3918	if (kret != KERN_SUCCESS)
3919	{
3920		panic("webdav_vnop_pagein: ubc_upl_map() failed with (%d)", kret);
3921	}
3922
3923	ioaddr += ap->a_pl_offset;	/* add a_pl_offset */
3924
3925	error = uio_addiov(auio, (user_addr_t)ioaddr, ap->a_size);
3926	if ( error )
3927	{
3928		goto exit;
3929	}
3930
3931	/* Ok, start the sleep loop to wait on the background download
3932	  We will know that the webdav user process is finished when it
3933	  either clears the nodump flag or sets the append only flag
3934	  (indicating an error) */
3935	tried_bytes = FALSE;
3936	do
3937	{
3938		VATTR_INIT(&attrbuf);
3939		VATTR_WANTED(&attrbuf, va_flags);
3940		VATTR_WANTED(&attrbuf, va_data_size);
3941		error = vnode_getattr(cachevp, &attrbuf, ap->a_context);
3942		if (error)
3943		{
3944			goto exit;
3945		}
3946
3947		if ((attrbuf.va_flags & UF_NODUMP) && (uio_offset(auio) + uio_resid(auio)) > (off_t)attrbuf.va_data_size)
3948		{
3949			struct timespec ts;
3950
3951			/* We are downloading the file and we haven't gotten to
3952			 * to the bytes we need so sleep, and then try the whole
3953			 * thing again.	We will take one shot at trying to get the
3954			 * bytes out of the file directly if that part hasn't yet
3955			 * been downloaded.	This is a little iffy since the VM system
3956			 * may now be chaching data that could theoritically be out of
3957			 * sync with what's on the server.  That is the following sequence
3958			 * of operations could lead to strange results:
3959			 * 1. I start a read and begin a down load
3960			 * 2. Another client changes the file
3961			 * 3. I do a byte read of the end of the file and get the new data
3962			 * 4. The download finishes and the underlying cache file has
3963			 *	 the old data, possibly depending on how the server works.
3964			 */
3965			if (!tried_bytes)
3966			{
3967				if ((uio_offset(auio) + uio_resid(auio)) > ((off_t)attrbuf.va_data_size + WEBDAV_WAIT_IF_WITHIN))
3968				{
3969					error = webdav_read_bytes(vp, auio, ap->a_context);
3970					if (!error)
3971					{
3972						if (uio_resid(auio) == 0)
3973						{
3974							goto exit;
3975						}
3976						else
3977						{
3978							/* we did not get all the data we wanted, we don't
3979							* know why so we'll just give up on the byte access
3980							* and wait for the data to download. We need to reset
3981							* the uio in that case since the VM system is not going
3982							* to be happy with partial reads
3983							*/
3984							uio_reset(auio, ap->a_f_offset, UIO_SYSSPACE, UIO_READ);
3985
3986							error = uio_addiov(auio, (user_addr_t)ioaddr, ap->a_size);
3987							if ( error )
3988							{
3989								goto exit;
3990							}
3991						}
3992					}
3993				}
3994
3995				/* If we are here, we must have failed to get the bytes so set
3996				 * tried_bytes so we won't make this mistake again and sleep */
3997				tried_bytes = TRUE;
3998			}
3999
4000			ts.tv_sec = 0;
4001			ts.tv_nsec = WEBDAV_WAIT_FOR_PAGE_TIME;
4002			error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_vnop_pagein", &ts);
4003			if ( error)
4004			{
4005				if ( error == EWOULDBLOCK )
4006				{
4007					error = 0;
4008				}
4009				else
4010				{
4011					printf("webdav_vnop_pagein: msleep(): %d\n", error);
4012					/* convert pseudo-errors to EIO */
4013					if ( error < 0 )
4014					{
4015						error = EIO;
4016					}
4017					goto exit;
4018				}
4019			}
4020		}
4021		else
4022		{
4023			/* the part we need has been downloaded */
4024			if ( pt->pt_filesize < (off_t)attrbuf.va_data_size )
4025			{
4026				/* keep the ubc size up to date */
4027				(void) ubc_setsize(vp, attrbuf.va_data_size); /* ignore failures - nothing can be done */
4028				pt->pt_filesize = (off_t)attrbuf.va_data_size;
4029			}
4030			break;
4031		}
4032	} while ( TRUE );
4033
4034	if (attrbuf.va_flags & UF_APPEND)
4035	{
4036		/* If the UF_APPEND flag is set, there was an error downloading the file from the
4037		 * server, so exit with an EIO result.
4038		 */
4039		error = EIO;
4040		goto exit;
4041	}
4042
4043	/* At this point, cachevp is locked and either the file is completely downloaded into
4044	 * cachevp, or the page this I/O ends within has been completely downloaded into cachevp.
4045	 */
4046
4047	if (ap->a_f_offset > (off_t)attrbuf.va_data_size)
4048	{
4049		/* Trying to pagein data beyond the eof is a no no */
4050		error = EFAULT;
4051		goto exit;
4052	}
4053
4054	error = VNOP_READ(cachevp, auio, ((ap->a_flags & UPL_IOSYNC) ? IO_SYNC : 0), ap->a_context);
4055
4056	if (uio_resid(auio) != 0)
4057	{
4058		/* If we were not able to read the entire page, check to
4059		 * see if we are at the end of the file, and if so, zero
4060		 * out the remaining part of the page
4061		 */
4062		if (attrbuf.va_data_size < (uint64_t)ap->a_f_offset + ap->a_size)
4063		{
4064			bytes_to_zero = ap->a_f_offset + ap->a_size - attrbuf.va_data_size;
4065			bzero(   (void *)(((uintptr_t) (ioaddr + ap->a_size - bytes_to_zero))), (size_t)bytes_to_zero);
4066		}
4067	}
4068
4069exit:
4070	kret = ubc_upl_unmap(ap->a_pl);
4071
4072	if (kret != KERN_SUCCESS)
4073	{
4074        panic("webdav_vnop_pagein: ubc_upl_unmap() failed with (%d)", kret);
4075	}
4076
4077exit_no_unmap:
4078	if ( auio != NULL )
4079	{
4080		uio_free(auio);
4081	}
4082
4083	if ( (ap->a_flags & UPL_NOCOMMIT) == 0 )
4084	{
4085		if (!error)
4086		{
4087			kret = ubc_upl_commit_range(ap->a_pl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_COMMIT_FREE_ON_EMPTY);
4088		}
4089		else
4090		{
4091			kret = ubc_upl_abort_range(ap->a_pl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4092		}
4093	}
4094
4095	RET_ERR("webdav_vnop_pagein", error);
4096}
4097
4098/*****************************************************************************/
4099
4100/*
4101 * webdav_vnop_pageout
4102 *
4103 * Page out (write) a page of a file from memory.
4104 *
4105 * Note:
4106 *		To aovid deadlock, the webdavnode lock is not taken here. The lock is already held
4107 *		by the originating webdav vnop.
4108 *
4109 *		For example, if were to lock the node in this pageout vnop,
4110 *		the ubc_setsize() call below could end up calling this very
4111 *		same vnop, resulting in deadlock.  See Radar 4749124.
4112 *
4113 * To do:
4114 *		If UBC ever supports an efficient way to move pages from the webdav
4115 *		UPL (a_pl) to the cache file's UPL, use that method instead of calling VOP_WRITE.
4116 */
4117static int webdav_vnop_pageout(struct vnop_pageout_args *ap)
4118/*
4119	struct vnop_pageout_args {
4120		struct vnodeop_desc *a_desc;
4121		vnode_t a_vp;
4122		upl_t a_pl;
4123		vm_offset_t a_pl_offset;
4124		off_t a_f_offset;
4125		size_t a_size;
4126		int a_flags;
4127		vfs_context_t a_context;
4128	};
4129*/
4130{
4131	vm_offset_t ioaddr;
4132	uio_t auio;
4133	vnode_t cachevp;
4134	vnode_t vp;
4135	struct webdavnode *pt;
4136	int error;
4137	kern_return_t kret;
4138	struct vnode_attr attrbuf;
4139	struct vnop_open_args open_ap;
4140	struct vnop_close_args close_ap;
4141	boolean_t is_open = FALSE;
4142
4143	START_MARKER("webdav_vnop_pageout");
4144
4145	vp = ap->a_vp;
4146	pt = VTOWEBDAV(vp);
4147
4148	cachevp = pt->pt_cache_vnode;
4149	error = 0;
4150	auio = NULL;
4151
4152	if (vnode_vfsisrdonly(vp))
4153	{
4154		error = EROFS;
4155		goto exit_no_unmap;
4156	}
4157
4158	// If the file is not opened (no cache file), then
4159	// temporarily open it for writing. Otherwise we will
4160	// cause a kernel panic when we reference cachevp below.
4161	// See Radar 6839215
4162	if (cachevp == NULLVP)
4163	{
4164		open_ap.a_desc = &vnop_open_desc;
4165		open_ap.a_vp = ap->a_vp;
4166		open_ap.a_mode = FWRITE;
4167		open_ap.a_context = ap->a_context;
4168
4169		error = webdav_vnop_open(&open_ap);
4170		cachevp = pt->pt_cache_vnode;
4171
4172		if ( (error) || (cachevp == NULLVP)) {
4173			printf("webdav_vnop_pageout: temp open failed, error: %d", error);
4174			error = EIO;
4175			goto exit_no_unmap;
4176		}
4177
4178		is_open = TRUE;
4179	}
4180
4181	auio = uio_create(1, ap->a_f_offset, UIO_SYSSPACE, UIO_WRITE);
4182	if ( auio == NULL )
4183	{
4184		error = EIO;
4185		goto exit_no_unmap;
4186	}
4187
4188	kret = ubc_upl_map(ap->a_pl, &ioaddr);
4189	if (kret != KERN_SUCCESS)
4190	{
4191        panic("webdav_vnop_pageout: ubc_upl_map() failed with (%d)", kret);
4192	}
4193
4194	ioaddr += ap->a_pl_offset;	/* add a_pl_offset */
4195
4196	error = uio_addiov(auio, (user_addr_t)ioaddr, ap->a_size);
4197	if ( error )
4198	{
4199		goto exit;
4200	}
4201
4202	do
4203	{
4204		VATTR_INIT(&attrbuf);
4205		VATTR_WANTED(&attrbuf, va_flags);
4206		VATTR_WANTED(&attrbuf, va_data_size);
4207		error = vnode_getattr(cachevp, &attrbuf, ap->a_context);
4208		if (error)
4209		{
4210			goto exit;
4211		}
4212
4213		if ((attrbuf.va_flags & UF_NODUMP) && (uio_offset(auio) + uio_resid(auio)) > (off_t)attrbuf.va_data_size)
4214		{
4215			struct timespec ts;
4216
4217			/* We are downloading the file and we haven't gotten to
4218			 * to the bytes we need so sleep, and then try the whole
4219			 * thing again.
4220			 */
4221			ts.tv_sec = 0;
4222			ts.tv_nsec = WEBDAV_WAIT_FOR_PAGE_TIME;
4223			error = msleep((caddr_t)&ts, NULL, PCATCH, "webdav_vnop_pageout", &ts);
4224			if ( error)
4225			{
4226				if ( error == EWOULDBLOCK )
4227				{
4228					error = 0;
4229				}
4230				else
4231				{
4232					printf("webdav_vnop_pageout: msleep(): %d\n", error);
4233					/* convert pseudo-errors to EIO */
4234					if ( error < 0 )
4235					{
4236						error = EIO;
4237					}
4238					goto exit;
4239				}
4240			}
4241		}
4242		else
4243		{
4244			/* the part we need has been downloaded */
4245			if ( pt->pt_filesize < (off_t)attrbuf.va_data_size )
4246			{
4247				/* keep the ubc size up to date */
4248				(void) ubc_setsize(vp, attrbuf.va_data_size); /* ignore failures - nothing can be done */
4249				pt->pt_filesize = (off_t)attrbuf.va_data_size;
4250			}
4251			break;
4252		}
4253	} while ( TRUE );
4254
4255	if (attrbuf.va_flags & UF_APPEND)
4256	{
4257		/* If the UF_APPEND flag is set, there was an error downloading the file from the
4258		 * server, so exit with an EIO result.
4259		 */
4260		error = EIO;
4261		goto exit;
4262	}
4263
4264	/* We don't want to write past the end of the file so
4265	 * truncate the write to the size.
4266	 */
4267	if (uio_offset(auio) + uio_resid(auio) > (off_t)attrbuf.va_data_size)
4268	{
4269		if (uio_offset(auio) < (off_t)attrbuf.va_data_size)
4270		{
4271			uio_setresid(auio, attrbuf.va_data_size - uio_offset(auio));
4272		}
4273		else
4274		{
4275			/* If we are here, someone probably truncated a file that
4276			 * someone else had mapped. In any event we are not allowed
4277			 * to grow the file on a page out so return EFAULT as that is
4278			 * what VM is expecting.
4279			 */
4280			error = EFAULT;
4281			goto exit;
4282		}
4283	}
4284
4285	error = VNOP_WRITE(cachevp, auio, ((ap->a_flags & UPL_IOSYNC) ? IO_SYNC : 0), ap->a_context);
4286
4287	/* after the write to the cache file has been completed... */
4288	pt->pt_status |= WEBDAV_DIRTY;
4289
4290exit:
4291	if ( auio != NULL )
4292	{
4293		uio_free(auio);
4294	}
4295
4296	kret = ubc_upl_unmap(ap->a_pl);
4297	if (kret != KERN_SUCCESS)
4298	{
4299		panic("webdav_vnop_pageout: ubc_upl_unmap() failed with (%d)", kret);
4300	}
4301
4302exit_no_unmap:
4303	if (is_open == TRUE) {
4304		close_ap.a_desc = &vnop_close_desc;
4305		close_ap.a_vp = ap->a_vp;
4306		close_ap.a_fflag = FWRITE;
4307		close_ap.a_context = ap->a_context;
4308		webdav_vnop_close(&close_ap);
4309	}
4310
4311	if ( (ap->a_flags & UPL_NOCOMMIT) == 0 )
4312	{
4313		if (!error)
4314		{
4315			kret = ubc_upl_commit_range(ap->a_pl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_COMMIT_FREE_ON_EMPTY);
4316		}
4317		else
4318		{
4319			kret = ubc_upl_abort_range(ap->a_pl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4320		}
4321	}
4322
4323	RET_ERR("webdav_vnop_pageout", error);
4324}
4325
4326/*****************************************************************************/
4327
4328static int webdav_vnop_ioctl(struct vnop_ioctl_args *ap)
4329/*
4330	struct vnop_ioctl_args {
4331		struct vnodeop_desc *a_desc;
4332		vnode_t a_vp;
4333		u_long a_command;
4334		caddr_t a_data;
4335		int a_fflag;
4336		vfs_context_t a_context;
4337	};
4338*/
4339{
4340	int error;
4341	vnode_t vp;
4342	struct webdavnode *pt;
4343	struct WebdavWriteSequential *wrseq_ptr;
4344
4345	START_MARKER("webdav_vnop_ioctl");
4346
4347	error = EINVAL;
4348	vp = ap->a_vp;
4349
4350	switch (ap->a_command)
4351	{
4352	case WEBDAV_INVALIDATECACHES:	/* invalidate all mount_webdav caches */
4353		{
4354			struct webdavmount *fmp;
4355			struct webdav_request_invalcaches request_invalcaches;
4356			int server_error;
4357
4358			/* Note: Since this command is coming through fsctl(), vnode_get has been called on the vnode */
4359
4360			/* set up the rest of the parameters needed to send a message */
4361			fmp = VFSTOWEBDAV(vnode_mount(vp));
4362			server_error = 0;
4363
4364			webdav_copy_creds(ap->a_context, &request_invalcaches.pcr);
4365
4366			error = webdav_sendmsg(WEBDAV_INVALCACHES, fmp,
4367				&request_invalcaches, sizeof(struct webdav_request_invalcaches),
4368				NULL, 0,
4369				&server_error, NULL, 0);
4370			if ( (error == 0) && (server_error != 0) )
4371			{
4372				error = server_error;
4373			}
4374		}
4375		break;
4376		case WEBDAV_WRITE_SEQUENTIAL:
4377			wrseq_ptr = (struct WebdavWriteSequential *)ap->a_data;
4378			pt = VTOWEBDAV(vp);
4379
4380			webdav_lock(pt, WEBDAV_EXCLUSIVE_LOCK);
4381			if (pt->pt_writeseq_enabled) {
4382#if DEBUG
4383				printf("KWRITESEQ: webdav_ioctl: writeseq mode already enabled\n");
4384#endif
4385				error = 0;
4386			} else if (pt->pt_opencount_write > 1) {
4387#if DEBUG
4388				printf("KWRITESEQ: webdav_ioctl: pt_opencount_write too high: %u\n", pt->pt_opencount_write);
4389#endif
4390				error = EBUSY;
4391			} else {
4392#if DEBUG
4393				printf("KWRITESEQ: webdav_ioctl: writesequential mode enabled, file_len %llu\n", wrseq_ptr->file_len);
4394#endif
4395				pt->pt_writeseq_enabled = 1;
4396				pt->pt_writeseq_len = wrseq_ptr->file_len;
4397				pt->pt_writeseq_offset = 0;
4398				error = 0;
4399			}
4400			webdav_unlock(pt);
4401		break;
4402
4403		case WEBDAV_SHOW_COOKIES:	/* dump cookie list */
4404		{
4405			struct webdavmount *fmp;
4406			struct webdav_request_cookies request_cookies;
4407			int server_error;
4408
4409			/* Note: Since this command is coming through fsctl(), vnode_get has been called on the vnode */
4410
4411			/* set up the rest of the parameters needed to send a message */
4412			fmp = VFSTOWEBDAV(vnode_mount(vp));
4413			server_error = 0;
4414
4415			webdav_copy_creds(ap->a_context, &request_cookies.pcr);
4416
4417			error = webdav_sendmsg(WEBDAV_DUMP_COOKIES, fmp,
4418								   &request_cookies, sizeof(struct webdav_request_cookies),
4419								   NULL, 0,
4420								   &server_error, NULL, 0);
4421			if ( (error == 0) && (server_error != 0) )
4422			{
4423				error = server_error;
4424			}
4425		}
4426		break;
4427
4428		case WEBDAV_RESET_COOKIES:	/* reset cookie list */
4429		{
4430			struct webdavmount *fmp;
4431			struct webdav_request_cookies request_cookies;
4432			int server_error;
4433
4434			/* Note: Since this command is coming through fsctl(), vnode_get has been called on the vnode */
4435
4436			/* set up the rest of the parameters needed to send a message */
4437			fmp = VFSTOWEBDAV(vnode_mount(vp));
4438			server_error = 0;
4439
4440			webdav_copy_creds(ap->a_context, &request_cookies.pcr);
4441
4442			error = webdav_sendmsg(WEBDAV_CLEAR_COOKIES, fmp,
4443								   &request_cookies, sizeof(struct webdav_request_cookies),
4444								   NULL, 0,
4445								   &server_error, NULL, 0);
4446			if ( (error == 0) && (server_error != 0) )
4447			{
4448				error = server_error;
4449			}
4450		}
4451		break;
4452
4453		default:
4454
4455		error = EINVAL;
4456		break;
4457	}
4458
4459	RET_ERR("webdav_vnop_ioctl", error);
4460}
4461
4462/*****************************************************************************/
4463
4464#define VOPFUNC int (*)(void *)
4465
4466int( **webdav_vnodeop_p)();
4467
4468struct vnodeopv_entry_desc webdav_vnodeop_entries[] = {
4469	{&vnop_default_desc, (VOPFUNC)vn_default_error},				/* default */
4470	{&vnop_lookup_desc, (VOPFUNC)webdav_vnop_lookup},				/* lookup */
4471	{&vnop_create_desc, (VOPFUNC)webdav_vnop_create},				/* create */
4472	{&vnop_open_desc, (VOPFUNC)webdav_vnop_open},					/* open */
4473	{&vnop_close_desc, (VOPFUNC)webdav_vnop_close},					/* close */
4474	{&vnop_getattr_desc, (VOPFUNC)webdav_vnop_getattr},				/* getattr */
4475	{&vnop_setattr_desc, (VOPFUNC)webdav_vnop_setattr},			/* setattr */
4476	{&vnop_read_desc, (VOPFUNC)webdav_vnop_read},					/* read */
4477	{&vnop_write_desc, (VOPFUNC)webdav_vnop_write},					/* write */
4478	{&vnop_ioctl_desc, (VOPFUNC)webdav_vnop_ioctl},					/* ioctl */
4479	{&vnop_mmap_desc, (VOPFUNC)webdav_vnop_mmap},					/* mmap */
4480	{&vnop_mnomap_desc, (VOPFUNC)webdav_vnop_mnomap},				/* mnomap */
4481	{&vnop_fsync_desc, (VOPFUNC)webdav_vnop_fsync},					/* fsync */
4482	{&vnop_remove_desc, (VOPFUNC)webdav_vnop_remove},				/* remove */
4483	{&vnop_rename_desc, (VOPFUNC)webdav_vnop_rename},				/* rename */
4484	{&vnop_mkdir_desc, (VOPFUNC)webdav_vnop_mkdir},					/* mkdir */
4485	{&vnop_rmdir_desc, (VOPFUNC)webdav_vnop_rmdir},					/* rmdir */
4486	{&vnop_readdir_desc, (VOPFUNC)webdav_vnop_readdir},				/* readdir */
4487	{&vnop_inactive_desc, (VOPFUNC)webdav_vnop_inactive},			/* inactive */
4488	{&vnop_reclaim_desc, (VOPFUNC)webdav_vnop_reclaim},				/* reclaim */
4489	{&vnop_pathconf_desc, (VOPFUNC)webdav_vnop_pathconf},			/* pathconf */
4490	{&vnop_pagein_desc, (VOPFUNC)webdav_vnop_pagein},				/* pagein */
4491	{&vnop_pageout_desc, (VOPFUNC)webdav_vnop_pageout},				/* pageout */
4492	{(struct vnodeop_desc *)NULL, (VOPFUNC)NULL}					/* end of table */
4493};
4494
4495struct vnodeopv_desc webdav_vnodeop_opv_desc = {
4496	&webdav_vnodeop_p, webdav_vnodeop_entries};
4497
4498/*****************************************************************************/
4499