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(¤ttime); 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(¤ttime); 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(¤ttime); 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