1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18** DAV filesystem lock implementation 19*/ 20 21#include "apr.h" 22#include "apr_strings.h" 23#include "apr_file_io.h" 24#include "apr_uuid.h" 25 26#define APR_WANT_MEMFUNC 27#include "apr_want.h" 28 29#include "httpd.h" 30#include "http_log.h" 31 32#include "mod_dav.h" 33#include "repos.h" 34 35 36/* --------------------------------------------------------------- 37** 38** Lock database primitives 39** 40*/ 41 42/* 43** LOCK DATABASES 44** 45** Lockdiscovery information is stored in the single lock database specified 46** by the DAVLockDB directive. Information about this db is stored in the 47** global server configuration. 48** 49** KEY 50** 51** The database is keyed by a key_type unsigned char (DAV_TYPE_INODE or 52** DAV_TYPE_FNAME) followed by inode and device number if possible, 53** otherwise full path (in the case of Win32 or lock-null resources). 54** 55** VALUE 56** 57** The value consists of a list of elements. 58** DIRECT LOCK: [char (DAV_LOCK_DIRECT), 59** char (dav_lock_scope), 60** char (dav_lock_type), 61** int depth, 62** time_t expires, 63** apr_uuid_t locktoken, 64** char[] owner, 65** char[] auth_user] 66** 67** INDIRECT LOCK: [char (DAV_LOCK_INDIRECT), 68** apr_uuid_t locktoken, 69** time_t expires, 70** apr_size_t key_size, 71** char[] key] 72** The key is to the collection lock that resulted in this indirect lock 73*/ 74 75#define DAV_TRUE 1 76#define DAV_FALSE 0 77 78#define DAV_CREATE_LIST 23 79#define DAV_APPEND_LIST 24 80 81/* Stored lock_discovery prefix */ 82#define DAV_LOCK_DIRECT 1 83#define DAV_LOCK_INDIRECT 2 84 85#define DAV_TYPE_INODE 10 86#define DAV_TYPE_FNAME 11 87 88 89/* ack. forward declare. */ 90static dav_error * dav_fs_remove_locknull_member(apr_pool_t *p, 91 const char *filename, 92 dav_buffer *pbuf); 93 94/* 95** Use the opaquelock scheme for locktokens 96*/ 97struct dav_locktoken { 98 apr_uuid_t uuid; 99}; 100#define dav_compare_locktoken(plt1, plt2) \ 101 memcmp(&(plt1)->uuid, &(plt2)->uuid, sizeof((plt1)->uuid)) 102 103 104/* ################################################################# 105** ### keep these structures (internal) or move fully to dav_lock? 106*/ 107 108/* 109** We need to reliably size the fixed-length portion of 110** dav_lock_discovery; best to separate it into another 111** struct for a convenient sizeof, unless we pack lock_discovery. 112*/ 113typedef struct dav_lock_discovery_fixed 114{ 115 char scope; 116 char type; 117 int depth; 118 time_t timeout; 119} dav_lock_discovery_fixed; 120 121typedef struct dav_lock_discovery 122{ 123 struct dav_lock_discovery_fixed f; 124 125 dav_locktoken *locktoken; 126 const char *owner; /* owner field from activelock */ 127 const char *auth_user; /* authenticated user who created the lock */ 128 struct dav_lock_discovery *next; 129} dav_lock_discovery; 130 131/* Indirect locks represent locks inherited from containing collections. 132 * They reference the lock token for the collection the lock is 133 * inherited from. A lock provider may also define a key to the 134 * inherited lock, for fast datbase lookup. The key is opaque outside 135 * the lock provider. 136 */ 137typedef struct dav_lock_indirect 138{ 139 dav_locktoken *locktoken; 140 apr_datum_t key; 141 struct dav_lock_indirect *next; 142 time_t timeout; 143} dav_lock_indirect; 144 145/* ################################################################# */ 146 147 148/* 149** Stored direct lock info - full lock_discovery length: 150** prefix + Fixed length + lock token + 2 strings + 2 nulls (one for each string) 151*/ 152#define dav_size_direct(a) ( 1 + sizeof(dav_lock_discovery_fixed) \ 153 + sizeof(apr_uuid_t) \ 154 + ((a)->owner ? strlen((a)->owner) : 0) \ 155 + ((a)->auth_user ? strlen((a)->auth_user) : 0) \ 156 + 2) 157 158/* Stored indirect lock info - lock token and apr_datum_t */ 159#define dav_size_indirect(a) (1 + sizeof(apr_uuid_t) \ 160 + sizeof(time_t) \ 161 + sizeof((a)->key.dsize) + (a)->key.dsize) 162 163/* 164** The lockdb structure. 165** 166** The <db> field may be NULL, meaning one of two things: 167** 1) That we have not actually opened the underlying database (yet). The 168** <opened> field should be false. 169** 2) We opened it readonly and it wasn't present. 170** 171** The delayed opening (determined by <opened>) makes creating a lockdb 172** quick, while deferring the underlying I/O until it is actually required. 173** 174** We export the notion of a lockdb, but hide the details of it. Most 175** implementations will use a database of some kind, but it is certainly 176** possible that alternatives could be used. 177*/ 178struct dav_lockdb_private 179{ 180 request_rec *r; /* for accessing the uuid state */ 181 apr_pool_t *pool; /* a pool to use */ 182 const char *lockdb_path; /* where is the lock database? */ 183 184 int opened; /* we opened the database */ 185 dav_db *db; /* if non-NULL, the lock database */ 186}; 187typedef struct 188{ 189 dav_lockdb pub; 190 dav_lockdb_private priv; 191} dav_lockdb_combined; 192 193/* 194** The private part of the lock structure. 195*/ 196struct dav_lock_private 197{ 198 apr_datum_t key; /* key into the lock database */ 199}; 200typedef struct 201{ 202 dav_lock pub; 203 dav_lock_private priv; 204 dav_locktoken token; 205} dav_lock_combined; 206 207/* 208** This must be forward-declared so the open_lockdb function can use it. 209*/ 210extern const dav_hooks_locks dav_hooks_locks_fs; 211 212 213/* internal function for creating locks */ 214static dav_lock *dav_fs_alloc_lock(dav_lockdb *lockdb, apr_datum_t key, 215 const dav_locktoken *locktoken) 216{ 217 dav_lock_combined *comb; 218 219 comb = apr_pcalloc(lockdb->info->pool, sizeof(*comb)); 220 comb->pub.rectype = DAV_LOCKREC_DIRECT; 221 comb->pub.info = &comb->priv; 222 comb->priv.key = key; 223 224 if (locktoken == NULL) { 225 comb->pub.locktoken = &comb->token; 226 apr_uuid_get(&comb->token.uuid); 227 } 228 else { 229 comb->pub.locktoken = locktoken; 230 } 231 232 return &comb->pub; 233} 234 235/* 236** dav_fs_parse_locktoken 237** 238** Parse an opaquelocktoken URI into a locktoken. 239*/ 240static dav_error * dav_fs_parse_locktoken( 241 apr_pool_t *p, 242 const char *char_token, 243 dav_locktoken **locktoken_p) 244{ 245 dav_locktoken *locktoken; 246 247 if (ap_strstr_c(char_token, "opaquelocktoken:") != char_token) { 248 return dav_new_error(p, 249 HTTP_BAD_REQUEST, DAV_ERR_LOCK_UNK_STATE_TOKEN, 250 "The lock token uses an unknown State-token " 251 "format and could not be parsed."); 252 } 253 char_token += 16; 254 255 locktoken = apr_pcalloc(p, sizeof(*locktoken)); 256 if (apr_uuid_parse(&locktoken->uuid, char_token)) { 257 return dav_new_error(p, HTTP_BAD_REQUEST, DAV_ERR_LOCK_PARSE_TOKEN, 258 "The opaquelocktoken has an incorrect format " 259 "and could not be parsed."); 260 } 261 262 *locktoken_p = locktoken; 263 return NULL; 264} 265 266/* 267** dav_fs_format_locktoken 268** 269** Generate the URI for a locktoken 270*/ 271static const char *dav_fs_format_locktoken( 272 apr_pool_t *p, 273 const dav_locktoken *locktoken) 274{ 275 char buf[APR_UUID_FORMATTED_LENGTH + 1]; 276 277 apr_uuid_format(buf, &locktoken->uuid); 278 return apr_pstrcat(p, "opaquelocktoken:", buf, NULL); 279} 280 281/* 282** dav_fs_compare_locktoken 283** 284** Determine whether two locktokens are the same 285*/ 286static int dav_fs_compare_locktoken( 287 const dav_locktoken *lt1, 288 const dav_locktoken *lt2) 289{ 290 return dav_compare_locktoken(lt1, lt2); 291} 292 293/* 294** dav_fs_really_open_lockdb: 295** 296** If the database hasn't been opened yet, then open the thing. 297*/ 298static dav_error * dav_fs_really_open_lockdb(dav_lockdb *lockdb) 299{ 300 dav_error *err; 301 302 if (lockdb->info->opened) 303 return NULL; 304 305 err = dav_dbm_open_direct(lockdb->info->pool, 306 lockdb->info->lockdb_path, 307 lockdb->ro, 308 &lockdb->info->db); 309 if (err != NULL) { 310 return dav_push_error(lockdb->info->pool, 311 HTTP_INTERNAL_SERVER_ERROR, 312 DAV_ERR_LOCK_OPENDB, 313 "Could not open the lock database.", 314 err); 315 } 316 317 /* all right. it is opened now. */ 318 lockdb->info->opened = 1; 319 320 return NULL; 321} 322 323/* 324** dav_fs_open_lockdb: 325** 326** "open" the lock database, as specified in the global server configuration. 327** If force is TRUE, then the database is opened now, rather than lazily. 328** 329** Note that only one can be open read/write. 330*/ 331static dav_error * dav_fs_open_lockdb(request_rec *r, int ro, int force, 332 dav_lockdb **lockdb) 333{ 334 dav_lockdb_combined *comb; 335 336 comb = apr_pcalloc(r->pool, sizeof(*comb)); 337 comb->pub.hooks = &dav_hooks_locks_fs; 338 comb->pub.ro = ro; 339 comb->pub.info = &comb->priv; 340 comb->priv.r = r; 341 comb->priv.pool = r->pool; 342 343 comb->priv.lockdb_path = dav_get_lockdb_path(r); 344 if (comb->priv.lockdb_path == NULL) { 345 return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 346 DAV_ERR_LOCK_NO_DB, 347 "A lock database was not specified with the " 348 "DAVLockDB directive. One must be specified " 349 "to use the locking functionality."); 350 } 351 352 /* done initializing. return it. */ 353 *lockdb = &comb->pub; 354 355 if (force) { 356 /* ### add a higher-level comment? */ 357 return dav_fs_really_open_lockdb(*lockdb); 358 } 359 360 return NULL; 361} 362 363/* 364** dav_fs_close_lockdb: 365** 366** Close it. Duh. 367*/ 368static void dav_fs_close_lockdb(dav_lockdb *lockdb) 369{ 370 if (lockdb->info->db != NULL) 371 dav_dbm_close(lockdb->info->db); 372} 373 374/* 375** dav_fs_build_fname_key 376** 377** Given a pathname, build a DAV_TYPE_FNAME lock database key. 378*/ 379static apr_datum_t dav_fs_build_fname_key(apr_pool_t *p, const char *pathname) 380{ 381 apr_datum_t key; 382 383 /* ### does this allocation have a proper lifetime? need to check */ 384 /* ### can we use a buffer for this? */ 385 386 /* size is TYPE + pathname + null */ 387 key.dsize = strlen(pathname) + 2; 388 key.dptr = apr_palloc(p, key.dsize); 389 *key.dptr = DAV_TYPE_FNAME; 390 memcpy(key.dptr + 1, pathname, key.dsize - 1); 391 if (key.dptr[key.dsize - 2] == '/') 392 key.dptr[--key.dsize - 1] = '\0'; 393 return key; 394} 395 396/* 397** dav_fs_build_key: Given a resource, return a apr_datum_t key 398** to look up lock information for this file. 399** 400** (inode/dev not supported or file is lock-null): 401** apr_datum_t->dvalue = full path 402** 403** (inode/dev supported and file exists ): 404** apr_datum_t->dvalue = inode, dev 405*/ 406static apr_datum_t dav_fs_build_key(apr_pool_t *p, 407 const dav_resource *resource) 408{ 409 const char *file = dav_fs_pathname(resource); 410 apr_datum_t key; 411 apr_finfo_t finfo; 412 apr_status_t rv; 413 414 /* ### use lstat() ?? */ 415 /* 416 * XXX: What for platforms with no IDENT (dev/inode)? 417 */ 418 rv = apr_stat(&finfo, file, APR_FINFO_IDENT, p); 419 if ((rv == APR_SUCCESS || rv == APR_INCOMPLETE) 420 && ((finfo.valid & APR_FINFO_IDENT) == APR_FINFO_IDENT)) 421 { 422 /* ### can we use a buffer for this? */ 423 key.dsize = 1 + sizeof(finfo.inode) + sizeof(finfo.device); 424 key.dptr = apr_palloc(p, key.dsize); 425 *key.dptr = DAV_TYPE_INODE; 426 memcpy(key.dptr + 1, &finfo.inode, sizeof(finfo.inode)); 427 memcpy(key.dptr + 1 + sizeof(finfo.inode), &finfo.device, 428 sizeof(finfo.device)); 429 430 return key; 431 } 432 433 return dav_fs_build_fname_key(p, file); 434} 435 436/* 437** dav_fs_lock_expired: return 1 (true) if the given timeout is in the past 438** or present (the lock has expired), or 0 (false) if in the future 439** (the lock has not yet expired). 440*/ 441static int dav_fs_lock_expired(time_t expires) 442{ 443 return expires != DAV_TIMEOUT_INFINITE && time(NULL) >= expires; 444} 445 446/* 447** dav_fs_save_lock_record: Saves the lock information specified in the 448** direct and indirect lock lists about path into the lock database. 449** If direct and indirect == NULL, the key is removed. 450*/ 451static dav_error * dav_fs_save_lock_record(dav_lockdb *lockdb, apr_datum_t key, 452 dav_lock_discovery *direct, 453 dav_lock_indirect *indirect) 454{ 455 dav_error *err; 456 apr_datum_t val = { 0 }; 457 char *ptr; 458 dav_lock_discovery *dp = direct; 459 dav_lock_indirect *ip = indirect; 460 461#if DAV_DEBUG 462 if (lockdb->ro) { 463 return dav_new_error(lockdb->info->pool, 464 HTTP_INTERNAL_SERVER_ERROR, 0, 465 "INTERNAL DESIGN ERROR: the lockdb was opened " 466 "readonly, but an attempt to save locks was " 467 "performed."); 468 } 469#endif 470 471 if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { 472 /* ### add a higher-level error? */ 473 return err; 474 } 475 476 /* If nothing to save, delete key */ 477 if (dp == NULL && ip == NULL) { 478 /* don't fail if the key is not present */ 479 /* ### but what about other errors? */ 480 (void) dav_dbm_delete(lockdb->info->db, key); 481 return NULL; 482 } 483 484 while(dp) { 485 val.dsize += dav_size_direct(dp); 486 dp = dp->next; 487 } 488 while(ip) { 489 val.dsize += dav_size_indirect(ip); 490 ip = ip->next; 491 } 492 493 /* ### can this be apr_palloc() ? */ 494 /* ### hmmm.... investigate the use of a buffer here */ 495 ptr = val.dptr = apr_pcalloc(lockdb->info->pool, val.dsize); 496 dp = direct; 497 ip = indirect; 498 499 while(dp) { 500 *ptr++ = DAV_LOCK_DIRECT; /* Direct lock - lock_discovery struct follows */ 501 memcpy(ptr, dp, sizeof(dp->f)); /* Fixed portion of struct */ 502 ptr += sizeof(dp->f); 503 memcpy(ptr, dp->locktoken, sizeof(*dp->locktoken)); 504 ptr += sizeof(*dp->locktoken); 505 if (dp->owner == NULL) { 506 *ptr++ = '\0'; 507 } 508 else { 509 memcpy(ptr, dp->owner, strlen(dp->owner) + 1); 510 ptr += strlen(dp->owner) + 1; 511 } 512 if (dp->auth_user == NULL) { 513 *ptr++ = '\0'; 514 } 515 else { 516 memcpy(ptr, dp->auth_user, strlen(dp->auth_user) + 1); 517 ptr += strlen(dp->auth_user) + 1; 518 } 519 520 dp = dp->next; 521 } 522 523 while(ip) { 524 *ptr++ = DAV_LOCK_INDIRECT; /* Indirect lock prefix */ 525 memcpy(ptr, ip->locktoken, sizeof(*ip->locktoken)); /* Locktoken */ 526 ptr += sizeof(*ip->locktoken); 527 memcpy(ptr, &ip->timeout, sizeof(ip->timeout)); /* Expire time */ 528 ptr += sizeof(ip->timeout); 529 memcpy(ptr, &ip->key.dsize, sizeof(ip->key.dsize)); /* Size of key */ 530 ptr += sizeof(ip->key.dsize); 531 memcpy(ptr, ip->key.dptr, ip->key.dsize); /* Key data */ 532 ptr += ip->key.dsize; 533 ip = ip->next; 534 } 535 536 if ((err = dav_dbm_store(lockdb->info->db, key, val)) != NULL) { 537 /* ### more details? add an error_id? */ 538 return dav_push_error(lockdb->info->pool, 539 HTTP_INTERNAL_SERVER_ERROR, 540 DAV_ERR_LOCK_SAVE_LOCK, 541 "Could not save lock information.", 542 err); 543 } 544 545 return NULL; 546} 547 548/* 549** dav_load_lock_record: Reads lock information about key from lock db; 550** creates linked lists of the direct and indirect locks. 551** 552** If add_method = DAV_APPEND_LIST, the result will be appended to the 553** head of the direct and indirect lists supplied. 554** 555** Passive lock removal: If lock has timed out, it will not be returned. 556** ### How much "logging" does RFC 2518 require? 557*/ 558static dav_error * dav_fs_load_lock_record(dav_lockdb *lockdb, apr_datum_t key, 559 int add_method, 560 dav_lock_discovery **direct, 561 dav_lock_indirect **indirect) 562{ 563 apr_pool_t *p = lockdb->info->pool; 564 dav_error *err; 565 apr_size_t offset = 0; 566 int need_save = DAV_FALSE; 567 apr_datum_t val = { 0 }; 568 dav_lock_discovery *dp; 569 dav_lock_indirect *ip; 570 dav_buffer buf = { 0 }; 571 572 if (add_method != DAV_APPEND_LIST) { 573 *direct = NULL; 574 *indirect = NULL; 575 } 576 577 if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { 578 /* ### add a higher-level error? */ 579 return err; 580 } 581 582 /* 583 ** If we opened readonly and the db wasn't there, then there are no 584 ** locks for this resource. Just exit. 585 */ 586 if (lockdb->info->db == NULL) 587 return NULL; 588 589 if ((err = dav_dbm_fetch(lockdb->info->db, key, &val)) != NULL) 590 return err; 591 592 if (!val.dsize) 593 return NULL; 594 595 while (offset < val.dsize) { 596 switch (*(val.dptr + offset++)) { 597 case DAV_LOCK_DIRECT: 598 /* Create and fill a dav_lock_discovery structure */ 599 600 dp = apr_pcalloc(p, sizeof(*dp)); 601 memcpy(dp, val.dptr + offset, sizeof(dp->f)); 602 offset += sizeof(dp->f); 603 dp->locktoken = apr_pmemdup(p, val.dptr + offset, sizeof(*dp->locktoken)); 604 offset += sizeof(*dp->locktoken); 605 if (*(val.dptr + offset) == '\0') { 606 ++offset; 607 } 608 else { 609 dp->owner = apr_pstrdup(p, val.dptr + offset); 610 offset += strlen(dp->owner) + 1; 611 } 612 613 if (*(val.dptr + offset) == '\0') { 614 ++offset; 615 } 616 else { 617 dp->auth_user = apr_pstrdup(p, val.dptr + offset); 618 offset += strlen(dp->auth_user) + 1; 619 } 620 621 if (!dav_fs_lock_expired(dp->f.timeout)) { 622 dp->next = *direct; 623 *direct = dp; 624 } 625 else { 626 need_save = DAV_TRUE; 627 628 /* Remove timed-out locknull fm .locknull list */ 629 if (*key.dptr == DAV_TYPE_FNAME) { 630 const char *fname = key.dptr + 1; 631 apr_finfo_t finfo; 632 apr_status_t rv; 633 634 /* if we don't see the file, then it's a locknull */ 635 rv = apr_stat(&finfo, fname, APR_FINFO_MIN | APR_FINFO_LINK, p); 636 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { 637 if ((err = dav_fs_remove_locknull_member(p, fname, &buf)) != NULL) { 638 /* ### push a higher-level description? */ 639 return err; 640 } 641 } 642 } 643 } 644 break; 645 646 case DAV_LOCK_INDIRECT: 647 /* Create and fill a dav_lock_indirect structure */ 648 649 ip = apr_pcalloc(p, sizeof(*ip)); 650 ip->locktoken = apr_pmemdup(p, val.dptr + offset, sizeof(*ip->locktoken)); 651 offset += sizeof(*ip->locktoken); 652 memcpy(&ip->timeout, val.dptr + offset, sizeof(ip->timeout)); 653 offset += sizeof(ip->timeout); 654 memcpy(&ip->key.dsize, val.dptr + offset, sizeof(ip->key.dsize)); /* length of datum */ 655 offset += sizeof(ip->key.dsize); 656 ip->key.dptr = apr_pmemdup(p, val.dptr + offset, ip->key.dsize); 657 offset += ip->key.dsize; 658 659 if (!dav_fs_lock_expired(ip->timeout)) { 660 ip->next = *indirect; 661 *indirect = ip; 662 } 663 else { 664 need_save = DAV_TRUE; 665 /* A locknull resource will never be locked indirectly */ 666 } 667 668 break; 669 670 default: 671 dav_dbm_freedatum(lockdb->info->db, val); 672 673 /* ### should use a computed_desc and insert corrupt token data */ 674 --offset; 675 return dav_new_error(p, 676 HTTP_INTERNAL_SERVER_ERROR, 677 DAV_ERR_LOCK_CORRUPT_DB, 678 apr_psprintf(p, 679 "The lock database was found to " 680 "be corrupt. offset %" 681 APR_SIZE_T_FMT ", c=%02x", 682 offset, val.dptr[offset])); 683 } 684 } 685 686 dav_dbm_freedatum(lockdb->info->db, val); 687 688 /* Clean up this record if we found expired locks */ 689 /* 690 ** ### shouldn't do this if we've been opened READONLY. elide the 691 ** ### timed-out locks from the response, but don't save that info back 692 */ 693 if (need_save == DAV_TRUE) { 694 return dav_fs_save_lock_record(lockdb, key, *direct, *indirect); 695 } 696 697 return NULL; 698} 699 700/* resolve <indirect>, returning <*direct> */ 701static dav_error * dav_fs_resolve(dav_lockdb *lockdb, 702 dav_lock_indirect *indirect, 703 dav_lock_discovery **direct, 704 dav_lock_discovery **ref_dp, 705 dav_lock_indirect **ref_ip) 706{ 707 dav_error *err; 708 dav_lock_discovery *dir; 709 dav_lock_indirect *ind; 710 711 if ((err = dav_fs_load_lock_record(lockdb, indirect->key, 712 DAV_CREATE_LIST, 713 &dir, &ind)) != NULL) { 714 /* ### insert a higher-level description? */ 715 return err; 716 } 717 if (ref_dp != NULL) { 718 *ref_dp = dir; 719 *ref_ip = ind; 720 } 721 722 for (; dir != NULL; dir = dir->next) { 723 if (!dav_compare_locktoken(indirect->locktoken, dir->locktoken)) { 724 *direct = dir; 725 return NULL; 726 } 727 } 728 729 /* No match found (but we should have found one!) */ 730 731 /* ### use a different description and/or error ID? */ 732 return dav_new_error(lockdb->info->pool, 733 HTTP_INTERNAL_SERVER_ERROR, 734 DAV_ERR_LOCK_CORRUPT_DB, 735 "The lock database was found to be corrupt. " 736 "An indirect lock's direct lock could not " 737 "be found."); 738} 739 740/* --------------------------------------------------------------- 741** 742** Property-related lock functions 743** 744*/ 745 746/* 747** dav_fs_get_supportedlock: Returns a static string for all supportedlock 748** properties. I think we save more returning a static string than 749** constructing it every time, though it might look cleaner. 750*/ 751static const char *dav_fs_get_supportedlock(const dav_resource *resource) 752{ 753 static const char supported[] = DEBUG_CR 754 "<D:lockentry>" DEBUG_CR 755 "<D:lockscope><D:exclusive/></D:lockscope>" DEBUG_CR 756 "<D:locktype><D:write/></D:locktype>" DEBUG_CR 757 "</D:lockentry>" DEBUG_CR 758 "<D:lockentry>" DEBUG_CR 759 "<D:lockscope><D:shared/></D:lockscope>" DEBUG_CR 760 "<D:locktype><D:write/></D:locktype>" DEBUG_CR 761 "</D:lockentry>" DEBUG_CR; 762 763 return supported; 764} 765 766/* --------------------------------------------------------------- 767** 768** General lock functions 769** 770*/ 771 772/* --------------------------------------------------------------- 773** 774** Functions dealing with lock-null resources 775** 776*/ 777 778/* 779** dav_fs_load_locknull_list: Returns a dav_buffer dump of the locknull file 780** for the given directory. 781*/ 782static dav_error * dav_fs_load_locknull_list(apr_pool_t *p, const char *dirpath, 783 dav_buffer *pbuf) 784{ 785 apr_finfo_t finfo; 786 apr_file_t *file = NULL; 787 dav_error *err = NULL; 788 apr_size_t amt; 789 apr_status_t rv; 790 791 dav_buffer_init(p, pbuf, dirpath); 792 793 if (pbuf->buf[pbuf->cur_len - 1] == '/') 794 pbuf->buf[--pbuf->cur_len] = '\0'; 795 796 dav_buffer_place(p, pbuf, "/" DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE); 797 798 /* reset this in case we leave w/o reading into the buffer */ 799 pbuf->cur_len = 0; 800 801 if (apr_file_open(&file, pbuf->buf, APR_READ | APR_BINARY, APR_OS_DEFAULT, 802 p) != APR_SUCCESS) { 803 return NULL; 804 } 805 806 rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file); 807 if (rv != APR_SUCCESS) { 808 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 809 apr_psprintf(p, 810 "Opened but could not stat file %s", 811 pbuf->buf)); 812 goto loaderror; 813 } 814 815 if (finfo.size != (apr_size_t)finfo.size) { 816 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 817 apr_psprintf(p, 818 "Opened but rejected huge file %s", 819 pbuf->buf)); 820 goto loaderror; 821 } 822 823 amt = (apr_size_t)finfo.size; 824 dav_set_bufsize(p, pbuf, amt); 825 if (apr_file_read(file, pbuf->buf, &amt) != APR_SUCCESS 826 || amt != finfo.size) { 827 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 828 apr_psprintf(p, 829 "Failure reading locknull file " 830 "for %s", dirpath)); 831 832 /* just in case the caller disregards the returned error */ 833 pbuf->cur_len = 0; 834 goto loaderror; 835 } 836 837 loaderror: 838 apr_file_close(file); 839 return err; 840} 841 842/* 843** dav_fs_save_locknull_list: Saves contents of pbuf into the 844** locknull file for dirpath. 845*/ 846static dav_error * dav_fs_save_locknull_list(apr_pool_t *p, const char *dirpath, 847 dav_buffer *pbuf) 848{ 849 const char *pathname; 850 apr_file_t *file = NULL; 851 dav_error *err = NULL; 852 apr_size_t amt; 853 854 if (pbuf->buf == NULL) 855 return NULL; 856 857 dav_fs_ensure_state_dir(p, dirpath); 858 pathname = apr_pstrcat(p, 859 dirpath, 860 dirpath[strlen(dirpath) - 1] == '/' ? "" : "/", 861 DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE, 862 NULL); 863 864 if (pbuf->cur_len == 0) { 865 /* delete the file if cur_len == 0 */ 866 if (apr_file_remove(pathname, p) != 0) { 867 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 868 apr_psprintf(p, 869 "Error removing %s", pathname)); 870 } 871 return NULL; 872 } 873 874 if (apr_file_open(&file, pathname, 875 APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY, 876 APR_OS_DEFAULT, p) != APR_SUCCESS) { 877 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 878 apr_psprintf(p, 879 "Error opening %s for writing", 880 pathname)); 881 } 882 883 amt = pbuf->cur_len; 884 if (apr_file_write(file, pbuf->buf, &amt) != APR_SUCCESS 885 || amt != pbuf->cur_len) { 886 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 887 apr_psprintf(p, 888 "Error writing %" APR_SIZE_T_FMT 889 " bytes to %s", 890 pbuf->cur_len, pathname)); 891 } 892 893 apr_file_close(file); 894 return err; 895} 896 897/* 898** dav_fs_remove_locknull_member: Removes filename from the locknull list 899** for directory path. 900*/ 901static dav_error * dav_fs_remove_locknull_member(apr_pool_t *p, 902 const char *filename, 903 dav_buffer *pbuf) 904{ 905 dav_error *err; 906 apr_size_t len; 907 apr_size_t scanlen; 908 char *scan; 909 const char *scanend; 910 char *dirpath = apr_pstrdup(p, filename); 911 char *fname = strrchr(dirpath, '/'); 912 int dirty = 0; 913 914 if (fname != NULL) 915 *fname++ = '\0'; 916 else 917 fname = dirpath; 918 len = strlen(fname) + 1; 919 920 if ((err = dav_fs_load_locknull_list(p, dirpath, pbuf)) != NULL) { 921 /* ### add a higher level description? */ 922 return err; 923 } 924 925 for (scan = pbuf->buf, scanend = scan + pbuf->cur_len; 926 scan < scanend; 927 scan += scanlen) { 928 scanlen = strlen(scan) + 1; 929 if (len == scanlen && memcmp(fname, scan, scanlen) == 0) { 930 pbuf->cur_len -= scanlen; 931 memmove(scan, scan + scanlen, scanend - (scan + scanlen)); 932 dirty = 1; 933 break; 934 } 935 } 936 937 if (dirty) { 938 if ((err = dav_fs_save_locknull_list(p, dirpath, pbuf)) != NULL) { 939 /* ### add a higher level description? */ 940 return err; 941 } 942 } 943 944 return NULL; 945} 946 947/* Note: used by dav_fs_repos.c */ 948dav_error * dav_fs_get_locknull_members( 949 const dav_resource *resource, 950 dav_buffer *pbuf) 951{ 952 const char *dirpath; 953 954 /* ### should test this result value... */ 955 (void) dav_fs_dir_file_name(resource, &dirpath, NULL); 956 return dav_fs_load_locknull_list(dav_fs_pool(resource), dirpath, pbuf); 957} 958 959/* ### fold into append_lock? */ 960/* ### take an optional buf parameter? */ 961static dav_error * dav_fs_add_locknull_state( 962 dav_lockdb *lockdb, 963 const dav_resource *resource) 964{ 965 dav_buffer buf = { 0 }; 966 apr_pool_t *p = lockdb->info->pool; 967 const char *dirpath; 968 const char *fname; 969 dav_error *err; 970 971 /* ### should test this result value... */ 972 (void) dav_fs_dir_file_name(resource, &dirpath, &fname); 973 974 if ((err = dav_fs_load_locknull_list(p, dirpath, &buf)) != NULL) { 975 return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 976 "Could not load .locknull file.", err); 977 } 978 979 dav_buffer_append(p, &buf, fname); 980 buf.cur_len++; /* we want the null-term here */ 981 982 if ((err = dav_fs_save_locknull_list(p, dirpath, &buf)) != NULL) { 983 return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 984 "Could not save .locknull file.", err); 985 } 986 987 return NULL; 988} 989 990/* 991** dav_fs_remove_locknull_state: Given a request, check to see if r->filename 992** is/was a lock-null resource. If so, return it to an existant state. 993** 994** ### this function is broken... it doesn't check! 995** 996** In this implementation, this involves two things: 997** (a) remove it from the list in the appropriate .DAV/locknull file 998** (b) on *nix, convert the key from a filename to an inode. 999*/ 1000static dav_error * dav_fs_remove_locknull_state( 1001 dav_lockdb *lockdb, 1002 const dav_resource *resource) 1003{ 1004 dav_buffer buf = { 0 }; 1005 dav_error *err; 1006 apr_pool_t *p = lockdb->info->pool; 1007 const char *pathname = dav_fs_pathname(resource); 1008 1009 if ((err = dav_fs_remove_locknull_member(p, pathname, &buf)) != NULL) { 1010 /* ### add a higher-level description? */ 1011 return err; 1012 } 1013 1014 { 1015 dav_lock_discovery *ld; 1016 dav_lock_indirect *id; 1017 apr_datum_t key; 1018 1019 /* 1020 ** Fetch the lock(s) that made the resource lock-null. Remove 1021 ** them under the filename key. Obtain the new inode key, and 1022 ** save the same lock information under it. 1023 */ 1024 key = dav_fs_build_fname_key(p, pathname); 1025 if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, 1026 &ld, &id)) != NULL) { 1027 /* ### insert a higher-level error description */ 1028 return err; 1029 } 1030 1031 if ((err = dav_fs_save_lock_record(lockdb, key, NULL, NULL)) != NULL) { 1032 /* ### insert a higher-level error description */ 1033 return err; 1034 } 1035 1036 key = dav_fs_build_key(p, resource); 1037 if ((err = dav_fs_save_lock_record(lockdb, key, ld, id)) != NULL) { 1038 /* ### insert a higher-level error description */ 1039 return err; 1040 } 1041 } 1042 1043 return NULL; 1044} 1045 1046static dav_error * dav_fs_create_lock(dav_lockdb *lockdb, 1047 const dav_resource *resource, 1048 dav_lock **lock) 1049{ 1050 apr_datum_t key; 1051 1052 key = dav_fs_build_key(lockdb->info->pool, resource); 1053 1054 *lock = dav_fs_alloc_lock(lockdb, 1055 key, 1056 NULL); 1057 1058 (*lock)->is_locknull = !resource->exists; 1059 1060 return NULL; 1061} 1062 1063static dav_error * dav_fs_get_locks(dav_lockdb *lockdb, 1064 const dav_resource *resource, 1065 int calltype, 1066 dav_lock **locks) 1067{ 1068 apr_pool_t *p = lockdb->info->pool; 1069 apr_datum_t key; 1070 dav_error *err; 1071 dav_lock *lock = NULL; 1072 dav_lock *newlock; 1073 dav_lock_discovery *dp; 1074 dav_lock_indirect *ip; 1075 1076#if DAV_DEBUG 1077 if (calltype == DAV_GETLOCKS_COMPLETE) { 1078 return dav_new_error(lockdb->info->pool, 1079 HTTP_INTERNAL_SERVER_ERROR, 0, 1080 "INTERNAL DESIGN ERROR: DAV_GETLOCKS_COMPLETE " 1081 "is not yet supported"); 1082 } 1083#endif 1084 1085 key = dav_fs_build_key(p, resource); 1086 if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, 1087 &dp, &ip)) != NULL) { 1088 /* ### push a higher-level desc? */ 1089 return err; 1090 } 1091 1092 /* copy all direct locks to the result list */ 1093 for (; dp != NULL; dp = dp->next) { 1094 newlock = dav_fs_alloc_lock(lockdb, key, dp->locktoken); 1095 newlock->is_locknull = !resource->exists; 1096 newlock->scope = dp->f.scope; 1097 newlock->type = dp->f.type; 1098 newlock->depth = dp->f.depth; 1099 newlock->timeout = dp->f.timeout; 1100 newlock->owner = dp->owner; 1101 newlock->auth_user = dp->auth_user; 1102 1103 /* hook into the result list */ 1104 newlock->next = lock; 1105 lock = newlock; 1106 } 1107 1108 /* copy all the indirect locks to the result list. resolve as needed. */ 1109 for (; ip != NULL; ip = ip->next) { 1110 newlock = dav_fs_alloc_lock(lockdb, ip->key, ip->locktoken); 1111 newlock->is_locknull = !resource->exists; 1112 1113 if (calltype == DAV_GETLOCKS_RESOLVED) { 1114 if ((err = dav_fs_resolve(lockdb, ip, &dp, NULL, NULL)) != NULL) { 1115 /* ### push a higher-level desc? */ 1116 return err; 1117 } 1118 1119 newlock->scope = dp->f.scope; 1120 newlock->type = dp->f.type; 1121 newlock->depth = dp->f.depth; 1122 newlock->timeout = dp->f.timeout; 1123 newlock->owner = dp->owner; 1124 newlock->auth_user = dp->auth_user; 1125 } 1126 else { 1127 /* DAV_GETLOCKS_PARTIAL */ 1128 newlock->rectype = DAV_LOCKREC_INDIRECT_PARTIAL; 1129 } 1130 1131 /* hook into the result list */ 1132 newlock->next = lock; 1133 lock = newlock; 1134 } 1135 1136 *locks = lock; 1137 return NULL; 1138} 1139 1140static dav_error * dav_fs_find_lock(dav_lockdb *lockdb, 1141 const dav_resource *resource, 1142 const dav_locktoken *locktoken, 1143 int partial_ok, 1144 dav_lock **lock) 1145{ 1146 dav_error *err; 1147 apr_datum_t key; 1148 dav_lock_discovery *dp; 1149 dav_lock_indirect *ip; 1150 1151 *lock = NULL; 1152 1153 key = dav_fs_build_key(lockdb->info->pool, resource); 1154 if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, 1155 &dp, &ip)) != NULL) { 1156 /* ### push a higher-level desc? */ 1157 return err; 1158 } 1159 1160 for (; dp != NULL; dp = dp->next) { 1161 if (!dav_compare_locktoken(locktoken, dp->locktoken)) { 1162 *lock = dav_fs_alloc_lock(lockdb, key, locktoken); 1163 (*lock)->is_locknull = !resource->exists; 1164 (*lock)->scope = dp->f.scope; 1165 (*lock)->type = dp->f.type; 1166 (*lock)->depth = dp->f.depth; 1167 (*lock)->timeout = dp->f.timeout; 1168 (*lock)->owner = dp->owner; 1169 (*lock)->auth_user = dp->auth_user; 1170 return NULL; 1171 } 1172 } 1173 1174 for (; ip != NULL; ip = ip->next) { 1175 if (!dav_compare_locktoken(locktoken, ip->locktoken)) { 1176 *lock = dav_fs_alloc_lock(lockdb, ip->key, locktoken); 1177 (*lock)->is_locknull = !resource->exists; 1178 1179 /* ### nobody uses the resolving right now! */ 1180 if (partial_ok) { 1181 (*lock)->rectype = DAV_LOCKREC_INDIRECT_PARTIAL; 1182 } 1183 else { 1184 (*lock)->rectype = DAV_LOCKREC_INDIRECT; 1185 if ((err = dav_fs_resolve(lockdb, ip, &dp, 1186 NULL, NULL)) != NULL) { 1187 /* ### push a higher-level desc? */ 1188 return err; 1189 } 1190 (*lock)->scope = dp->f.scope; 1191 (*lock)->type = dp->f.type; 1192 (*lock)->depth = dp->f.depth; 1193 (*lock)->timeout = dp->f.timeout; 1194 (*lock)->owner = dp->owner; 1195 (*lock)->auth_user = dp->auth_user; 1196 } 1197 return NULL; 1198 } 1199 } 1200 1201 return NULL; 1202} 1203 1204static dav_error * dav_fs_has_locks(dav_lockdb *lockdb, 1205 const dav_resource *resource, 1206 int *locks_present) 1207{ 1208 dav_error *err; 1209 apr_datum_t key; 1210 1211 *locks_present = 0; 1212 1213 if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { 1214 /* ### insert a higher-level error description */ 1215 return err; 1216 } 1217 1218 /* 1219 ** If we opened readonly and the db wasn't there, then there are no 1220 ** locks for this resource. Just exit. 1221 */ 1222 if (lockdb->info->db == NULL) 1223 return NULL; 1224 1225 key = dav_fs_build_key(lockdb->info->pool, resource); 1226 1227 *locks_present = dav_dbm_exists(lockdb->info->db, key); 1228 1229 return NULL; 1230} 1231 1232static dav_error * dav_fs_append_locks(dav_lockdb *lockdb, 1233 const dav_resource *resource, 1234 int make_indirect, 1235 const dav_lock *lock) 1236{ 1237 apr_pool_t *p = lockdb->info->pool; 1238 dav_error *err; 1239 dav_lock_indirect *ip; 1240 dav_lock_discovery *dp; 1241 apr_datum_t key; 1242 1243 key = dav_fs_build_key(lockdb->info->pool, resource); 1244 if ((err = dav_fs_load_lock_record(lockdb, key, 0, &dp, &ip)) != NULL) { 1245 /* ### maybe add in a higher-level description */ 1246 return err; 1247 } 1248 1249 /* 1250 ** ### when we store the lock more directly, we need to update 1251 ** ### lock->rectype and lock->is_locknull 1252 */ 1253 1254 if (make_indirect) { 1255 for (; lock != NULL; lock = lock->next) { 1256 1257 /* ### this works for any <lock> rectype */ 1258 dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi)); 1259 1260 /* ### shut off the const warning for now */ 1261 newi->locktoken = (dav_locktoken *)lock->locktoken; 1262 newi->timeout = lock->timeout; 1263 newi->key = lock->info->key; 1264 newi->next = ip; 1265 ip = newi; 1266 } 1267 } 1268 else { 1269 for (; lock != NULL; lock = lock->next) { 1270 /* create and link in the right kind of lock */ 1271 1272 if (lock->rectype == DAV_LOCKREC_DIRECT) { 1273 dav_lock_discovery *newd = apr_pcalloc(p, sizeof(*newd)); 1274 1275 newd->f.scope = lock->scope; 1276 newd->f.type = lock->type; 1277 newd->f.depth = lock->depth; 1278 newd->f.timeout = lock->timeout; 1279 /* ### shut off the const warning for now */ 1280 newd->locktoken = (dav_locktoken *)lock->locktoken; 1281 newd->owner = lock->owner; 1282 newd->auth_user = lock->auth_user; 1283 newd->next = dp; 1284 dp = newd; 1285 } 1286 else { 1287 /* DAV_LOCKREC_INDIRECT(_PARTIAL) */ 1288 1289 dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi)); 1290 1291 /* ### shut off the const warning for now */ 1292 newi->locktoken = (dav_locktoken *)lock->locktoken; 1293 newi->key = lock->info->key; 1294 newi->next = ip; 1295 ip = newi; 1296 } 1297 } 1298 } 1299 1300 if ((err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) { 1301 /* ### maybe add a higher-level description */ 1302 return err; 1303 } 1304 1305 /* we have a special list for recording locknull resources */ 1306 /* ### ack! this can add two copies to the locknull list */ 1307 if (!resource->exists 1308 && (err = dav_fs_add_locknull_state(lockdb, resource)) != NULL) { 1309 /* ### maybe add a higher-level description */ 1310 return err; 1311 } 1312 1313 return NULL; 1314} 1315 1316static dav_error * dav_fs_remove_lock(dav_lockdb *lockdb, 1317 const dav_resource *resource, 1318 const dav_locktoken *locktoken) 1319{ 1320 dav_error *err; 1321 dav_buffer buf = { 0 }; 1322 dav_lock_discovery *dh = NULL; 1323 dav_lock_indirect *ih = NULL; 1324 apr_datum_t key; 1325 1326 key = dav_fs_build_key(lockdb->info->pool, resource); 1327 1328 if (locktoken != NULL) { 1329 dav_lock_discovery *dp; 1330 dav_lock_discovery *dprev = NULL; 1331 dav_lock_indirect *ip; 1332 dav_lock_indirect *iprev = NULL; 1333 1334 if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, 1335 &dh, &ih)) != NULL) { 1336 /* ### maybe add a higher-level description */ 1337 return err; 1338 } 1339 1340 for (dp = dh; dp != NULL; dp = dp->next) { 1341 if (dav_compare_locktoken(locktoken, dp->locktoken) == 0) { 1342 if (dprev) 1343 dprev->next = dp->next; 1344 else 1345 dh = dh->next; 1346 } 1347 dprev = dp; 1348 } 1349 1350 for (ip = ih; ip != NULL; ip = ip->next) { 1351 if (dav_compare_locktoken(locktoken, ip->locktoken) == 0) { 1352 if (iprev) 1353 iprev->next = ip->next; 1354 else 1355 ih = ih->next; 1356 } 1357 iprev = ip; 1358 } 1359 1360 } 1361 1362 /* save the modified locks, or remove all locks (dh=ih=NULL). */ 1363 if ((err = dav_fs_save_lock_record(lockdb, key, dh, ih)) != NULL) { 1364 /* ### maybe add a higher-level description */ 1365 return err; 1366 } 1367 1368 /* 1369 ** If this resource is a locknull resource AND no more locks exist, 1370 ** then remove the locknull member. 1371 ** 1372 ** Note: remove_locknull_state() attempts to convert a locknull member 1373 ** to a real member. In this case, all locks are gone, so the 1374 ** locknull resource returns to the null state (ie. doesn't exist), 1375 ** so there is no need to update the lockdb (and it won't find 1376 ** any because a precondition is that none exist). 1377 */ 1378 if (!resource->exists && dh == NULL && ih == NULL 1379 && (err = dav_fs_remove_locknull_member(lockdb->info->pool, 1380 dav_fs_pathname(resource), 1381 &buf)) != NULL) { 1382 /* ### maybe add a higher-level description */ 1383 return err; 1384 } 1385 1386 return NULL; 1387} 1388 1389static int dav_fs_do_refresh(dav_lock_discovery *dp, 1390 const dav_locktoken_list *ltl, 1391 time_t new_time) 1392{ 1393 int dirty = 0; 1394 1395 for (; ltl != NULL; ltl = ltl->next) { 1396 if (dav_compare_locktoken(dp->locktoken, ltl->locktoken) == 0) 1397 { 1398 dp->f.timeout = new_time; 1399 dirty = 1; 1400 } 1401 } 1402 1403 return dirty; 1404} 1405 1406static dav_error * dav_fs_refresh_locks(dav_lockdb *lockdb, 1407 const dav_resource *resource, 1408 const dav_locktoken_list *ltl, 1409 time_t new_time, 1410 dav_lock **locks) 1411{ 1412 dav_error *err; 1413 apr_datum_t key; 1414 dav_lock_discovery *dp; 1415 dav_lock_discovery *dp_scan; 1416 dav_lock_indirect *ip; 1417 int dirty = 0; 1418 dav_lock *newlock; 1419 1420 *locks = NULL; 1421 1422 key = dav_fs_build_key(lockdb->info->pool, resource); 1423 if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, 1424 &dp, &ip)) != NULL) { 1425 /* ### maybe add in a higher-level description */ 1426 return err; 1427 } 1428 1429 /* ### we should be refreshing direct AND (resolved) indirect locks! */ 1430 1431 /* refresh all of the direct locks on this resource */ 1432 for (dp_scan = dp; dp_scan != NULL; dp_scan = dp_scan->next) { 1433 if (dav_fs_do_refresh(dp_scan, ltl, new_time)) { 1434 /* the lock was refreshed. return the lock. */ 1435 newlock = dav_fs_alloc_lock(lockdb, key, dp_scan->locktoken); 1436 newlock->is_locknull = !resource->exists; 1437 newlock->scope = dp_scan->f.scope; 1438 newlock->type = dp_scan->f.type; 1439 newlock->depth = dp_scan->f.depth; 1440 newlock->timeout = dp_scan->f.timeout; 1441 newlock->owner = dp_scan->owner; 1442 newlock->auth_user = dp_scan->auth_user; 1443 1444 newlock->next = *locks; 1445 *locks = newlock; 1446 1447 dirty = 1; 1448 } 1449 } 1450 1451 /* if we refreshed any locks, then save them back. */ 1452 if (dirty 1453 && (err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) { 1454 /* ### maybe add in a higher-level description */ 1455 return err; 1456 } 1457 1458 /* for each indirect lock, find its direct lock and refresh it. */ 1459 for (; ip != NULL; ip = ip->next) { 1460 dav_lock_discovery *ref_dp; 1461 dav_lock_indirect *ref_ip; 1462 1463 if ((err = dav_fs_resolve(lockdb, ip, &dp_scan, 1464 &ref_dp, &ref_ip)) != NULL) { 1465 /* ### push a higher-level desc? */ 1466 return err; 1467 } 1468 if (dav_fs_do_refresh(dp_scan, ltl, new_time)) { 1469 /* the lock was refreshed. return the lock. */ 1470 newlock = dav_fs_alloc_lock(lockdb, ip->key, dp_scan->locktoken); 1471 newlock->is_locknull = !resource->exists; 1472 newlock->scope = dp_scan->f.scope; 1473 newlock->type = dp_scan->f.type; 1474 newlock->depth = dp_scan->f.depth; 1475 newlock->timeout = dp_scan->f.timeout; 1476 newlock->owner = dp_scan->owner; 1477 newlock->auth_user = dp_scan->auth_user; 1478 1479 newlock->next = *locks; 1480 *locks = newlock; 1481 1482 /* save the (resolved) direct lock back */ 1483 if ((err = dav_fs_save_lock_record(lockdb, ip->key, ref_dp, 1484 ref_ip)) != NULL) { 1485 /* ### push a higher-level desc? */ 1486 return err; 1487 } 1488 } 1489 } 1490 1491 return NULL; 1492} 1493 1494 1495const dav_hooks_locks dav_hooks_locks_fs = 1496{ 1497 dav_fs_get_supportedlock, 1498 dav_fs_parse_locktoken, 1499 dav_fs_format_locktoken, 1500 dav_fs_compare_locktoken, 1501 dav_fs_open_lockdb, 1502 dav_fs_close_lockdb, 1503 dav_fs_remove_locknull_state, 1504 dav_fs_create_lock, 1505 dav_fs_get_locks, 1506 dav_fs_find_lock, 1507 dav_fs_has_locks, 1508 dav_fs_append_locks, 1509 dav_fs_remove_lock, 1510 dav_fs_refresh_locks, 1511 NULL, /* lookup_resource */ 1512 1513 NULL /* ctx */ 1514}; 1515