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