~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/dav/fs/lock.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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. */
 
90
static 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
*/
 
97
struct 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
*/
 
113
typedef 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
 
 
121
typedef 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
 */
 
137
typedef 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
*/
 
178
struct 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
};
 
187
typedef 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
*/
 
196
struct dav_lock_private
 
197
{
 
198
    apr_datum_t key;   /* key into the lock database */
 
199
};
 
200
typedef 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
*/
 
210
extern const dav_hooks_locks dav_hooks_locks_fs;
 
211
 
 
212
 
 
213
/* internal function for creating locks */
 
214
static 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
*/
 
240
static 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
*/
 
271
static 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
*/
 
286
static 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
*/
 
298
static 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
*/
 
331
static 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
*/
 
368
static 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
*/
 
379
static 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
*/
 
406
static 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
*/
 
441
static 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
*/
 
451
static 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
*/
 
558
static 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_palloc(p, sizeof(*dp->locktoken));
 
604
            memcpy(dp->locktoken, val.dptr + offset, sizeof(*dp->locktoken));
 
605
            offset += sizeof(*dp->locktoken);
 
606
            if (*(val.dptr + offset) == '\0') {
 
607
                ++offset;
 
608
            }
 
609
            else {
 
610
                dp->owner = apr_pstrdup(p, val.dptr + offset);
 
611
                offset += strlen(dp->owner) + 1;
 
612
            }
 
613
 
 
614
            if (*(val.dptr + offset) == '\0') {
 
615
                ++offset;
 
616
            }
 
617
            else {
 
618
                dp->auth_user = apr_pstrdup(p, val.dptr + offset);
 
619
                offset += strlen(dp->auth_user) + 1;
 
620
            }
 
621
 
 
622
            if (!dav_fs_lock_expired(dp->f.timeout)) {
 
623
                dp->next = *direct;
 
624
                *direct = dp;
 
625
            }
 
626
            else {
 
627
                need_save = DAV_TRUE;
 
628
 
 
629
                /* Remove timed-out locknull fm .locknull list */
 
630
                if (*key.dptr == DAV_TYPE_FNAME) {
 
631
                    const char *fname = key.dptr + 1;
 
632
                    apr_finfo_t finfo;
 
633
                    apr_status_t rv;
 
634
 
 
635
                    /* if we don't see the file, then it's a locknull */
 
636
                    rv = apr_stat(&finfo, fname, APR_FINFO_MIN | APR_FINFO_LINK, p);
 
637
                    if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
 
638
                        if ((err = dav_fs_remove_locknull_member(p, fname, &buf)) != NULL) {
 
639
                            /* ### push a higher-level description? */
 
640
                            return err;
 
641
                        }
 
642
                    }
 
643
                }
 
644
            }
 
645
            break;
 
646
 
 
647
        case DAV_LOCK_INDIRECT:
 
648
            /* Create and fill a dav_lock_indirect structure */
 
649
 
 
650
            ip = apr_pcalloc(p, sizeof(*ip));
 
651
            ip->locktoken = apr_palloc(p, sizeof(*ip->locktoken));
 
652
            memcpy(ip->locktoken, val.dptr + offset, sizeof(*ip->locktoken));
 
653
            offset += sizeof(*ip->locktoken);
 
654
            memcpy(&ip->timeout, val.dptr + offset, sizeof(ip->timeout));
 
655
            offset += sizeof(ip->timeout);
 
656
            memcpy(&ip->key.dsize, val.dptr + offset, sizeof(ip->key.dsize)); /* length of datum */
 
657
            offset += sizeof(ip->key.dsize);
 
658
            ip->key.dptr = apr_palloc(p, ip->key.dsize);
 
659
            memcpy(ip->key.dptr, val.dptr + offset, ip->key.dsize);
 
660
            offset += ip->key.dsize;
 
661
 
 
662
            if (!dav_fs_lock_expired(ip->timeout)) {
 
663
                ip->next = *indirect;
 
664
                *indirect = ip;
 
665
            }
 
666
            else {
 
667
                need_save = DAV_TRUE;
 
668
                /* A locknull resource will never be locked indirectly */
 
669
            }
 
670
 
 
671
            break;
 
672
 
 
673
        default:
 
674
            dav_dbm_freedatum(lockdb->info->db, val);
 
675
 
 
676
            /* ### should use a computed_desc and insert corrupt token data */
 
677
            --offset;
 
678
            return dav_new_error(p,
 
679
                                 HTTP_INTERNAL_SERVER_ERROR,
 
680
                                 DAV_ERR_LOCK_CORRUPT_DB,
 
681
                                 apr_psprintf(p,
 
682
                                             "The lock database was found to "
 
683
                                             "be corrupt. offset %"
 
684
                                             APR_SIZE_T_FMT ", c=%02x",
 
685
                                             offset, val.dptr[offset]));
 
686
        }
 
687
    }
 
688
 
 
689
    dav_dbm_freedatum(lockdb->info->db, val);
 
690
 
 
691
    /* Clean up this record if we found expired locks */
 
692
    /*
 
693
    ** ### shouldn't do this if we've been opened READONLY. elide the
 
694
    ** ### timed-out locks from the response, but don't save that info back
 
695
    */
 
696
    if (need_save == DAV_TRUE) {
 
697
        return dav_fs_save_lock_record(lockdb, key, *direct, *indirect);
 
698
    }
 
699
 
 
700
    return NULL;
 
701
}
 
702
 
 
703
/* resolve <indirect>, returning <*direct> */
 
704
static dav_error * dav_fs_resolve(dav_lockdb *lockdb,
 
705
                                  dav_lock_indirect *indirect,
 
706
                                  dav_lock_discovery **direct,
 
707
                                  dav_lock_discovery **ref_dp,
 
708
                                  dav_lock_indirect **ref_ip)
 
709
{
 
710
    dav_error *err;
 
711
    dav_lock_discovery *dir;
 
712
    dav_lock_indirect *ind;
 
713
 
 
714
    if ((err = dav_fs_load_lock_record(lockdb, indirect->key,
 
715
                                       DAV_CREATE_LIST,
 
716
                                       &dir, &ind)) != NULL) {
 
717
        /* ### insert a higher-level description? */
 
718
        return err;
 
719
    }
 
720
    if (ref_dp != NULL) {
 
721
        *ref_dp = dir;
 
722
        *ref_ip = ind;
 
723
    }
 
724
 
 
725
    for (; dir != NULL; dir = dir->next) {
 
726
        if (!dav_compare_locktoken(indirect->locktoken, dir->locktoken)) {
 
727
            *direct = dir;
 
728
            return NULL;
 
729
        }
 
730
    }
 
731
 
 
732
    /* No match found (but we should have found one!) */
 
733
 
 
734
    /* ### use a different description and/or error ID? */
 
735
    return dav_new_error(lockdb->info->pool,
 
736
                         HTTP_INTERNAL_SERVER_ERROR,
 
737
                         DAV_ERR_LOCK_CORRUPT_DB,
 
738
                         "The lock database was found to be corrupt. "
 
739
                         "An indirect lock's direct lock could not "
 
740
                         "be found.");
 
741
}
 
742
 
 
743
/* ---------------------------------------------------------------
 
744
**
 
745
** Property-related lock functions
 
746
**
 
747
*/
 
748
 
 
749
/*
 
750
** dav_fs_get_supportedlock:  Returns a static string for all supportedlock
 
751
**    properties. I think we save more returning a static string than
 
752
**    constructing it every time, though it might look cleaner.
 
753
*/
 
754
static const char *dav_fs_get_supportedlock(const dav_resource *resource)
 
755
{
 
756
    static const char supported[] = DEBUG_CR
 
757
        "<D:lockentry>" DEBUG_CR
 
758
        "<D:lockscope><D:exclusive/></D:lockscope>" DEBUG_CR
 
759
        "<D:locktype><D:write/></D:locktype>" DEBUG_CR
 
760
        "</D:lockentry>" DEBUG_CR
 
761
        "<D:lockentry>" DEBUG_CR
 
762
        "<D:lockscope><D:shared/></D:lockscope>" DEBUG_CR
 
763
        "<D:locktype><D:write/></D:locktype>" DEBUG_CR
 
764
        "</D:lockentry>" DEBUG_CR;
 
765
 
 
766
    return supported;
 
767
}
 
768
 
 
769
/* ---------------------------------------------------------------
 
770
**
 
771
** General lock functions
 
772
**
 
773
*/
 
774
 
 
775
/* ---------------------------------------------------------------
 
776
**
 
777
** Functions dealing with lock-null resources
 
778
**
 
779
*/
 
780
 
 
781
/*
 
782
** dav_fs_load_locknull_list:  Returns a dav_buffer dump of the locknull file
 
783
**    for the given directory.
 
784
*/
 
785
static dav_error * dav_fs_load_locknull_list(apr_pool_t *p, const char *dirpath,
 
786
                                             dav_buffer *pbuf)
 
787
{
 
788
    apr_finfo_t finfo;
 
789
    apr_file_t *file = NULL;
 
790
    dav_error *err = NULL;
 
791
    apr_size_t amt;
 
792
    apr_status_t rv;
 
793
 
 
794
    dav_buffer_init(p, pbuf, dirpath);
 
795
 
 
796
    if (pbuf->buf[pbuf->cur_len - 1] == '/')
 
797
        pbuf->buf[--pbuf->cur_len] = '\0';
 
798
 
 
799
    dav_buffer_place(p, pbuf, "/" DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE);
 
800
 
 
801
    /* reset this in case we leave w/o reading into the buffer */
 
802
    pbuf->cur_len = 0;
 
803
 
 
804
    if (apr_file_open(&file, pbuf->buf, APR_READ | APR_BINARY, APR_OS_DEFAULT,
 
805
                p) != APR_SUCCESS) {
 
806
        return NULL;
 
807
    }
 
808
 
 
809
    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file);
 
810
    if (rv != APR_SUCCESS) {
 
811
        err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
812
                            apr_psprintf(p,
 
813
                                        "Opened but could not stat file %s",
 
814
                                        pbuf->buf));
 
815
        goto loaderror;
 
816
    }
 
817
 
 
818
    if (finfo.size != (apr_size_t)finfo.size) {
 
819
        err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
820
                            apr_psprintf(p,
 
821
                                        "Opened but rejected huge file %s",
 
822
                                        pbuf->buf));
 
823
        goto loaderror;
 
824
    }
 
825
 
 
826
    amt = (apr_size_t)finfo.size;
 
827
    dav_set_bufsize(p, pbuf, amt);
 
828
    if (apr_file_read(file, pbuf->buf, &amt) != APR_SUCCESS
 
829
        || amt != finfo.size) {
 
830
        err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
831
                            apr_psprintf(p,
 
832
                                        "Failure reading locknull file "
 
833
                                        "for %s", dirpath));
 
834
 
 
835
        /* just in case the caller disregards the returned error */
 
836
        pbuf->cur_len = 0;
 
837
        goto loaderror;
 
838
    }
 
839
 
 
840
  loaderror:
 
841
    apr_file_close(file);
 
842
    return err;
 
843
}
 
844
 
 
845
/*
 
846
** dav_fs_save_locknull_list:  Saves contents of pbuf into the
 
847
**    locknull file for dirpath.
 
848
*/
 
849
static dav_error * dav_fs_save_locknull_list(apr_pool_t *p, const char *dirpath,
 
850
                                             dav_buffer *pbuf)
 
851
{
 
852
    const char *pathname;
 
853
    apr_file_t *file = NULL;
 
854
    dav_error *err = NULL;
 
855
    apr_size_t amt;
 
856
 
 
857
    if (pbuf->buf == NULL)
 
858
        return NULL;
 
859
 
 
860
    dav_fs_ensure_state_dir(p, dirpath);
 
861
    pathname = apr_pstrcat(p,
 
862
                          dirpath,
 
863
                          dirpath[strlen(dirpath) - 1] == '/' ? "" : "/",
 
864
                          DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE,
 
865
                          NULL);
 
866
 
 
867
    if (pbuf->cur_len == 0) {
 
868
        /* delete the file if cur_len == 0 */
 
869
        if (apr_file_remove(pathname, p) != 0) {
 
870
            return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
871
                                 apr_psprintf(p,
 
872
                                             "Error removing %s", pathname));
 
873
        }
 
874
        return NULL;
 
875
    }
 
876
 
 
877
    if (apr_file_open(&file, pathname,
 
878
                APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY,
 
879
                APR_OS_DEFAULT, p) != APR_SUCCESS) {
 
880
        return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
881
                             apr_psprintf(p,
 
882
                                         "Error opening %s for writing",
 
883
                                         pathname));
 
884
    }
 
885
 
 
886
    amt = pbuf->cur_len;
 
887
    if (apr_file_write(file, pbuf->buf, &amt) != APR_SUCCESS
 
888
        || amt != pbuf->cur_len) {
 
889
        err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
890
                            apr_psprintf(p,
 
891
                                        "Error writing %" APR_SIZE_T_FMT
 
892
                                        " bytes to %s",
 
893
                                        pbuf->cur_len, pathname));
 
894
    }
 
895
 
 
896
    apr_file_close(file);
 
897
    return err;
 
898
}
 
899
 
 
900
/*
 
901
** dav_fs_remove_locknull_member:  Removes filename from the locknull list
 
902
**    for directory path.
 
903
*/
 
904
static dav_error * dav_fs_remove_locknull_member(apr_pool_t *p,
 
905
                                                 const char *filename,
 
906
                                                 dav_buffer *pbuf)
 
907
{
 
908
    dav_error *err;
 
909
    apr_size_t len;
 
910
    apr_size_t scanlen;
 
911
    char *scan;
 
912
    const char *scanend;
 
913
    char *dirpath = apr_pstrdup(p, filename);
 
914
    char *fname = strrchr(dirpath, '/');
 
915
    int dirty = 0;
 
916
 
 
917
    if (fname != NULL)
 
918
        *fname++ = '\0';
 
919
    else
 
920
        fname = dirpath;
 
921
    len = strlen(fname) + 1;
 
922
 
 
923
    if ((err = dav_fs_load_locknull_list(p, dirpath, pbuf)) != NULL) {
 
924
        /* ### add a higher level description? */
 
925
        return err;
 
926
    }
 
927
 
 
928
    for (scan = pbuf->buf, scanend = scan + pbuf->cur_len;
 
929
         scan < scanend;
 
930
         scan += scanlen) {
 
931
        scanlen = strlen(scan) + 1;
 
932
        if (len == scanlen && memcmp(fname, scan, scanlen) == 0) {
 
933
            pbuf->cur_len -= scanlen;
 
934
            memmove(scan, scan + scanlen, scanend - (scan + scanlen));
 
935
            dirty = 1;
 
936
            break;
 
937
        }
 
938
    }
 
939
 
 
940
    if (dirty) {
 
941
        if ((err = dav_fs_save_locknull_list(p, dirpath, pbuf)) != NULL) {
 
942
            /* ### add a higher level description? */
 
943
            return err;
 
944
        }
 
945
    }
 
946
 
 
947
    return NULL;
 
948
}
 
949
 
 
950
/* Note: used by dav_fs_repos.c */
 
951
dav_error * dav_fs_get_locknull_members(
 
952
    const dav_resource *resource,
 
953
    dav_buffer *pbuf)
 
954
{
 
955
    const char *dirpath;
 
956
 
 
957
    /* ### should test this result value... */
 
958
    (void) dav_fs_dir_file_name(resource, &dirpath, NULL);
 
959
    return dav_fs_load_locknull_list(dav_fs_pool(resource), dirpath, pbuf);
 
960
}
 
961
 
 
962
/* ### fold into append_lock? */
 
963
/* ### take an optional buf parameter? */
 
964
static dav_error * dav_fs_add_locknull_state(
 
965
    dav_lockdb *lockdb,
 
966
    const dav_resource *resource)
 
967
{
 
968
    dav_buffer buf = { 0 };
 
969
    apr_pool_t *p = lockdb->info->pool;
 
970
    const char *dirpath;
 
971
    const char *fname;
 
972
    dav_error *err;
 
973
 
 
974
    /* ### should test this result value... */
 
975
    (void) dav_fs_dir_file_name(resource, &dirpath, &fname);
 
976
 
 
977
    if ((err = dav_fs_load_locknull_list(p, dirpath, &buf)) != NULL) {
 
978
        return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
979
                              "Could not load .locknull file.", err);
 
980
    }
 
981
 
 
982
    dav_buffer_append(p, &buf, fname);
 
983
    buf.cur_len++;   /* we want the null-term here */
 
984
 
 
985
    if ((err = dav_fs_save_locknull_list(p, dirpath, &buf)) != NULL) {
 
986
        return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
987
                              "Could not save .locknull file.", err);
 
988
    }
 
989
 
 
990
    return NULL;
 
991
}
 
992
 
 
993
/*
 
994
** dav_fs_remove_locknull_state:  Given a request, check to see if r->filename
 
995
**    is/was a lock-null resource.  If so, return it to an existant state.
 
996
**
 
997
**    ### this function is broken... it doesn't check!
 
998
**
 
999
**    In this implementation, this involves two things:
 
1000
**    (a) remove it from the list in the appropriate .DAV/locknull file
 
1001
**    (b) on *nix, convert the key from a filename to an inode.
 
1002
*/
 
1003
static dav_error * dav_fs_remove_locknull_state(
 
1004
    dav_lockdb *lockdb,
 
1005
    const dav_resource *resource)
 
1006
{
 
1007
    dav_buffer buf = { 0 };
 
1008
    dav_error *err;
 
1009
    apr_pool_t *p = lockdb->info->pool;
 
1010
    const char *pathname = dav_fs_pathname(resource);
 
1011
 
 
1012
    if ((err = dav_fs_remove_locknull_member(p, pathname, &buf)) != NULL) {
 
1013
        /* ### add a higher-level description? */
 
1014
        return err;
 
1015
    }
 
1016
 
 
1017
    {
 
1018
        dav_lock_discovery *ld;
 
1019
        dav_lock_indirect  *id;
 
1020
        apr_datum_t key;
 
1021
 
 
1022
        /*
 
1023
        ** Fetch the lock(s) that made the resource lock-null. Remove
 
1024
        ** them under the filename key. Obtain the new inode key, and
 
1025
        ** save the same lock information under it.
 
1026
        */
 
1027
        key = dav_fs_build_fname_key(p, pathname);
 
1028
        if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
 
1029
                                           &ld, &id)) != NULL) {
 
1030
            /* ### insert a higher-level error description */
 
1031
            return err;
 
1032
        }
 
1033
 
 
1034
        if ((err = dav_fs_save_lock_record(lockdb, key, NULL, NULL)) != NULL) {
 
1035
            /* ### insert a higher-level error description */
 
1036
            return err;
 
1037
        }
 
1038
 
 
1039
        key = dav_fs_build_key(p, resource);
 
1040
        if ((err = dav_fs_save_lock_record(lockdb, key, ld, id)) != NULL) {
 
1041
            /* ### insert a higher-level error description */
 
1042
            return err;
 
1043
        }
 
1044
    }
 
1045
 
 
1046
    return NULL;
 
1047
}
 
1048
 
 
1049
static dav_error * dav_fs_create_lock(dav_lockdb *lockdb,
 
1050
                                      const dav_resource *resource,
 
1051
                                      dav_lock **lock)
 
1052
{
 
1053
    apr_datum_t key;
 
1054
 
 
1055
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1056
 
 
1057
    *lock = dav_fs_alloc_lock(lockdb,
 
1058
                              key,
 
1059
                              NULL);
 
1060
 
 
1061
    (*lock)->is_locknull = !resource->exists;
 
1062
 
 
1063
    return NULL;
 
1064
}
 
1065
 
 
1066
static dav_error * dav_fs_get_locks(dav_lockdb *lockdb,
 
1067
                                    const dav_resource *resource,
 
1068
                                    int calltype,
 
1069
                                    dav_lock **locks)
 
1070
{
 
1071
    apr_pool_t *p = lockdb->info->pool;
 
1072
    apr_datum_t key;
 
1073
    dav_error *err;
 
1074
    dav_lock *lock = NULL;
 
1075
    dav_lock *newlock;
 
1076
    dav_lock_discovery *dp;
 
1077
    dav_lock_indirect *ip;
 
1078
 
 
1079
#if DAV_DEBUG
 
1080
    if (calltype == DAV_GETLOCKS_COMPLETE) {
 
1081
        return dav_new_error(lockdb->info->pool,
 
1082
                             HTTP_INTERNAL_SERVER_ERROR, 0,
 
1083
                             "INTERNAL DESIGN ERROR: DAV_GETLOCKS_COMPLETE "
 
1084
                             "is not yet supported");
 
1085
    }
 
1086
#endif
 
1087
 
 
1088
    key = dav_fs_build_key(p, resource);
 
1089
    if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
 
1090
                                       &dp, &ip)) != NULL) {
 
1091
        /* ### push a higher-level desc? */
 
1092
        return err;
 
1093
    }
 
1094
 
 
1095
    /* copy all direct locks to the result list */
 
1096
    for (; dp != NULL; dp = dp->next) {
 
1097
        newlock = dav_fs_alloc_lock(lockdb, key, dp->locktoken);
 
1098
        newlock->is_locknull = !resource->exists;
 
1099
        newlock->scope = dp->f.scope;
 
1100
        newlock->type = dp->f.type;
 
1101
        newlock->depth = dp->f.depth;
 
1102
        newlock->timeout = dp->f.timeout;
 
1103
        newlock->owner = dp->owner;
 
1104
        newlock->auth_user = dp->auth_user;
 
1105
 
 
1106
        /* hook into the result list */
 
1107
        newlock->next = lock;
 
1108
        lock = newlock;
 
1109
    }
 
1110
 
 
1111
    /* copy all the indirect locks to the result list. resolve as needed. */
 
1112
    for (; ip != NULL; ip = ip->next) {
 
1113
        newlock = dav_fs_alloc_lock(lockdb, ip->key, ip->locktoken);
 
1114
        newlock->is_locknull = !resource->exists;
 
1115
 
 
1116
        if (calltype == DAV_GETLOCKS_RESOLVED) {
 
1117
            if ((err = dav_fs_resolve(lockdb, ip, &dp, NULL, NULL)) != NULL) {
 
1118
                /* ### push a higher-level desc? */
 
1119
                return err;
 
1120
            }
 
1121
 
 
1122
            newlock->scope = dp->f.scope;
 
1123
            newlock->type = dp->f.type;
 
1124
            newlock->depth = dp->f.depth;
 
1125
            newlock->timeout = dp->f.timeout;
 
1126
            newlock->owner = dp->owner;
 
1127
            newlock->auth_user = dp->auth_user;
 
1128
        }
 
1129
        else {
 
1130
            /* DAV_GETLOCKS_PARTIAL */
 
1131
            newlock->rectype = DAV_LOCKREC_INDIRECT_PARTIAL;
 
1132
        }
 
1133
 
 
1134
        /* hook into the result list */
 
1135
        newlock->next = lock;
 
1136
        lock = newlock;
 
1137
    }
 
1138
 
 
1139
    *locks = lock;
 
1140
    return NULL;
 
1141
}
 
1142
 
 
1143
static dav_error * dav_fs_find_lock(dav_lockdb *lockdb,
 
1144
                                    const dav_resource *resource,
 
1145
                                    const dav_locktoken *locktoken,
 
1146
                                    int partial_ok,
 
1147
                                    dav_lock **lock)
 
1148
{
 
1149
    dav_error *err;
 
1150
    apr_datum_t key;
 
1151
    dav_lock_discovery *dp;
 
1152
    dav_lock_indirect *ip;
 
1153
 
 
1154
    *lock = NULL;
 
1155
 
 
1156
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1157
    if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
 
1158
                                       &dp, &ip)) != NULL) {
 
1159
        /* ### push a higher-level desc? */
 
1160
        return err;
 
1161
    }
 
1162
 
 
1163
    for (; dp != NULL; dp = dp->next) {
 
1164
        if (!dav_compare_locktoken(locktoken, dp->locktoken)) {
 
1165
            *lock = dav_fs_alloc_lock(lockdb, key, locktoken);
 
1166
            (*lock)->is_locknull = !resource->exists;
 
1167
            (*lock)->scope = dp->f.scope;
 
1168
            (*lock)->type = dp->f.type;
 
1169
            (*lock)->depth = dp->f.depth;
 
1170
            (*lock)->timeout = dp->f.timeout;
 
1171
            (*lock)->owner = dp->owner;
 
1172
            (*lock)->auth_user = dp->auth_user;
 
1173
            return NULL;
 
1174
        }
 
1175
    }
 
1176
 
 
1177
    for (; ip != NULL; ip = ip->next) {
 
1178
        if (!dav_compare_locktoken(locktoken, ip->locktoken)) {
 
1179
            *lock = dav_fs_alloc_lock(lockdb, ip->key, locktoken);
 
1180
            (*lock)->is_locknull = !resource->exists;
 
1181
 
 
1182
            /* ### nobody uses the resolving right now! */
 
1183
            if (partial_ok) {
 
1184
                (*lock)->rectype = DAV_LOCKREC_INDIRECT_PARTIAL;
 
1185
            }
 
1186
            else {
 
1187
                (*lock)->rectype = DAV_LOCKREC_INDIRECT;
 
1188
                if ((err = dav_fs_resolve(lockdb, ip, &dp,
 
1189
                                          NULL, NULL)) != NULL) {
 
1190
                    /* ### push a higher-level desc? */
 
1191
                    return err;
 
1192
                }
 
1193
                (*lock)->scope = dp->f.scope;
 
1194
                (*lock)->type = dp->f.type;
 
1195
                (*lock)->depth = dp->f.depth;
 
1196
                (*lock)->timeout = dp->f.timeout;
 
1197
                (*lock)->owner = dp->owner;
 
1198
                (*lock)->auth_user = dp->auth_user;
 
1199
            }
 
1200
            return NULL;
 
1201
        }
 
1202
    }
 
1203
 
 
1204
    return NULL;
 
1205
}
 
1206
 
 
1207
static dav_error * dav_fs_has_locks(dav_lockdb *lockdb,
 
1208
                                    const dav_resource *resource,
 
1209
                                    int *locks_present)
 
1210
{
 
1211
    dav_error *err;
 
1212
    apr_datum_t key;
 
1213
 
 
1214
    *locks_present = 0;
 
1215
 
 
1216
    if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) {
 
1217
        /* ### insert a higher-level error description */
 
1218
        return err;
 
1219
    }
 
1220
 
 
1221
    /*
 
1222
    ** If we opened readonly and the db wasn't there, then there are no
 
1223
    ** locks for this resource. Just exit.
 
1224
    */
 
1225
    if (lockdb->info->db == NULL)
 
1226
        return NULL;
 
1227
 
 
1228
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1229
 
 
1230
    *locks_present = dav_dbm_exists(lockdb->info->db, key);
 
1231
 
 
1232
    return NULL;
 
1233
}
 
1234
 
 
1235
static dav_error * dav_fs_append_locks(dav_lockdb *lockdb,
 
1236
                                       const dav_resource *resource,
 
1237
                                       int make_indirect,
 
1238
                                       const dav_lock *lock)
 
1239
{
 
1240
    apr_pool_t *p = lockdb->info->pool;
 
1241
    dav_error *err;
 
1242
    dav_lock_indirect *ip;
 
1243
    dav_lock_discovery *dp;
 
1244
    apr_datum_t key;
 
1245
 
 
1246
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1247
    if ((err = dav_fs_load_lock_record(lockdb, key, 0, &dp, &ip)) != NULL) {
 
1248
        /* ### maybe add in a higher-level description */
 
1249
        return err;
 
1250
    }
 
1251
 
 
1252
    /*
 
1253
    ** ### when we store the lock more directly, we need to update
 
1254
    ** ### lock->rectype and lock->is_locknull
 
1255
    */
 
1256
 
 
1257
    if (make_indirect) {
 
1258
        for (; lock != NULL; lock = lock->next) {
 
1259
 
 
1260
            /* ### this works for any <lock> rectype */
 
1261
            dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi));
 
1262
 
 
1263
            /* ### shut off the const warning for now */
 
1264
            newi->locktoken = (dav_locktoken *)lock->locktoken;
 
1265
            newi->timeout   = lock->timeout;
 
1266
            newi->key       = lock->info->key;
 
1267
            newi->next      = ip;
 
1268
            ip              = newi;
 
1269
        }
 
1270
    }
 
1271
    else {
 
1272
        for (; lock != NULL; lock = lock->next) {
 
1273
            /* create and link in the right kind of lock */
 
1274
 
 
1275
            if (lock->rectype == DAV_LOCKREC_DIRECT) {
 
1276
                dav_lock_discovery *newd = apr_pcalloc(p, sizeof(*newd));
 
1277
 
 
1278
                newd->f.scope = lock->scope;
 
1279
                newd->f.type = lock->type;
 
1280
                newd->f.depth = lock->depth;
 
1281
                newd->f.timeout = lock->timeout;
 
1282
                /* ### shut off the const warning for now */
 
1283
                newd->locktoken = (dav_locktoken *)lock->locktoken;
 
1284
                newd->owner = lock->owner;
 
1285
                newd->auth_user = lock->auth_user;
 
1286
                newd->next = dp;
 
1287
                dp = newd;
 
1288
            }
 
1289
            else {
 
1290
                /* DAV_LOCKREC_INDIRECT(_PARTIAL) */
 
1291
 
 
1292
                dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi));
 
1293
 
 
1294
                /* ### shut off the const warning for now */
 
1295
                newi->locktoken = (dav_locktoken *)lock->locktoken;
 
1296
                newi->key       = lock->info->key;
 
1297
                newi->next      = ip;
 
1298
                ip              = newi;
 
1299
            }
 
1300
        }
 
1301
    }
 
1302
 
 
1303
    if ((err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) {
 
1304
        /* ### maybe add a higher-level description */
 
1305
        return err;
 
1306
    }
 
1307
 
 
1308
    /* we have a special list for recording locknull resources */
 
1309
    /* ### ack! this can add two copies to the locknull list */
 
1310
    if (!resource->exists
 
1311
        && (err = dav_fs_add_locknull_state(lockdb, resource)) != NULL) {
 
1312
        /* ### maybe add a higher-level description */
 
1313
        return err;
 
1314
    }
 
1315
 
 
1316
    return NULL;
 
1317
}
 
1318
 
 
1319
static dav_error * dav_fs_remove_lock(dav_lockdb *lockdb,
 
1320
                                      const dav_resource *resource,
 
1321
                                      const dav_locktoken *locktoken)
 
1322
{
 
1323
    dav_error *err;
 
1324
    dav_buffer buf = { 0 };
 
1325
    dav_lock_discovery *dh = NULL;
 
1326
    dav_lock_indirect *ih = NULL;
 
1327
    apr_datum_t key;
 
1328
 
 
1329
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1330
 
 
1331
    if (locktoken != NULL) {
 
1332
        dav_lock_discovery *dp;
 
1333
        dav_lock_discovery *dprev = NULL;
 
1334
        dav_lock_indirect *ip;
 
1335
        dav_lock_indirect *iprev = NULL;
 
1336
 
 
1337
        if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
 
1338
                                           &dh, &ih)) != NULL) {
 
1339
            /* ### maybe add a higher-level description */
 
1340
            return err;
 
1341
        }
 
1342
 
 
1343
        for (dp = dh; dp != NULL; dp = dp->next) {
 
1344
            if (dav_compare_locktoken(locktoken, dp->locktoken) == 0) {
 
1345
                if (dprev)
 
1346
                    dprev->next = dp->next;
 
1347
                else
 
1348
                    dh = dh->next;
 
1349
            }
 
1350
            dprev = dp;
 
1351
        }
 
1352
 
 
1353
        for (ip = ih; ip != NULL; ip = ip->next) {
 
1354
            if (dav_compare_locktoken(locktoken, ip->locktoken) == 0) {
 
1355
                if (iprev)
 
1356
                    iprev->next = ip->next;
 
1357
                else
 
1358
                    ih = ih->next;
 
1359
            }
 
1360
            iprev = ip;
 
1361
        }
 
1362
 
 
1363
    }
 
1364
 
 
1365
    /* save the modified locks, or remove all locks (dh=ih=NULL). */
 
1366
    if ((err = dav_fs_save_lock_record(lockdb, key, dh, ih)) != NULL) {
 
1367
        /* ### maybe add a higher-level description */
 
1368
        return err;
 
1369
    }
 
1370
 
 
1371
    /*
 
1372
    ** If this resource is a locknull resource AND no more locks exist,
 
1373
    ** then remove the locknull member.
 
1374
    **
 
1375
    ** Note: remove_locknull_state() attempts to convert a locknull member
 
1376
    **       to a real member. In this case, all locks are gone, so the
 
1377
    **       locknull resource returns to the null state (ie. doesn't exist),
 
1378
    **       so there is no need to update the lockdb (and it won't find
 
1379
    **       any because a precondition is that none exist).
 
1380
    */
 
1381
    if (!resource->exists && dh == NULL && ih == NULL
 
1382
        && (err = dav_fs_remove_locknull_member(lockdb->info->pool,
 
1383
                                                dav_fs_pathname(resource),
 
1384
                                                &buf)) != NULL) {
 
1385
        /* ### maybe add a higher-level description */
 
1386
        return err;
 
1387
    }
 
1388
 
 
1389
    return NULL;
 
1390
}
 
1391
 
 
1392
static int dav_fs_do_refresh(dav_lock_discovery *dp,
 
1393
                             const dav_locktoken_list *ltl,
 
1394
                             time_t new_time)
 
1395
{
 
1396
    int dirty = 0;
 
1397
 
 
1398
    for (; ltl != NULL; ltl = ltl->next) {
 
1399
        if (dav_compare_locktoken(dp->locktoken, ltl->locktoken) == 0)
 
1400
        {
 
1401
            dp->f.timeout = new_time;
 
1402
            dirty = 1;
 
1403
        }
 
1404
    }
 
1405
 
 
1406
    return dirty;
 
1407
}
 
1408
 
 
1409
static dav_error * dav_fs_refresh_locks(dav_lockdb *lockdb,
 
1410
                                        const dav_resource *resource,
 
1411
                                        const dav_locktoken_list *ltl,
 
1412
                                        time_t new_time,
 
1413
                                        dav_lock **locks)
 
1414
{
 
1415
    dav_error *err;
 
1416
    apr_datum_t key;
 
1417
    dav_lock_discovery *dp;
 
1418
    dav_lock_discovery *dp_scan;
 
1419
    dav_lock_indirect *ip;
 
1420
    int dirty = 0;
 
1421
    dav_lock *newlock;
 
1422
 
 
1423
    *locks = NULL;
 
1424
 
 
1425
    key = dav_fs_build_key(lockdb->info->pool, resource);
 
1426
    if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
 
1427
                                       &dp, &ip)) != NULL) {
 
1428
        /* ### maybe add in a higher-level description */
 
1429
        return err;
 
1430
    }
 
1431
 
 
1432
    /* ### we should be refreshing direct AND (resolved) indirect locks! */
 
1433
 
 
1434
    /* refresh all of the direct locks on this resource */
 
1435
    for (dp_scan = dp; dp_scan != NULL; dp_scan = dp_scan->next) {
 
1436
        if (dav_fs_do_refresh(dp_scan, ltl, new_time)) {
 
1437
            /* the lock was refreshed. return the lock. */
 
1438
            newlock = dav_fs_alloc_lock(lockdb, key, dp_scan->locktoken);
 
1439
            newlock->is_locknull = !resource->exists;
 
1440
            newlock->scope = dp_scan->f.scope;
 
1441
            newlock->type = dp_scan->f.type;
 
1442
            newlock->depth = dp_scan->f.depth;
 
1443
            newlock->timeout = dp_scan->f.timeout;
 
1444
            newlock->owner = dp_scan->owner;
 
1445
            newlock->auth_user = dp_scan->auth_user;
 
1446
 
 
1447
            newlock->next = *locks;
 
1448
            *locks = newlock;
 
1449
 
 
1450
            dirty = 1;
 
1451
        }
 
1452
    }
 
1453
 
 
1454
    /* if we refreshed any locks, then save them back. */
 
1455
    if (dirty
 
1456
        && (err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) {
 
1457
        /* ### maybe add in a higher-level description */
 
1458
        return err;
 
1459
    }
 
1460
 
 
1461
    /* for each indirect lock, find its direct lock and refresh it. */
 
1462
    for (; ip != NULL; ip = ip->next) {
 
1463
        dav_lock_discovery *ref_dp;
 
1464
        dav_lock_indirect *ref_ip;
 
1465
 
 
1466
        if ((err = dav_fs_resolve(lockdb, ip, &dp_scan,
 
1467
                                  &ref_dp, &ref_ip)) != NULL) {
 
1468
            /* ### push a higher-level desc? */
 
1469
            return err;
 
1470
        }
 
1471
        if (dav_fs_do_refresh(dp_scan, ltl, new_time)) {
 
1472
            /* the lock was refreshed. return the lock. */
 
1473
            newlock = dav_fs_alloc_lock(lockdb, ip->key, dp_scan->locktoken);
 
1474
            newlock->is_locknull = !resource->exists;
 
1475
            newlock->scope = dp_scan->f.scope;
 
1476
            newlock->type = dp_scan->f.type;
 
1477
            newlock->depth = dp_scan->f.depth;
 
1478
            newlock->timeout = dp_scan->f.timeout;
 
1479
            newlock->owner = dp_scan->owner;
 
1480
            newlock->auth_user = dp_scan->auth_user;
 
1481
 
 
1482
            newlock->next = *locks;
 
1483
            *locks = newlock;
 
1484
 
 
1485
            /* save the (resolved) direct lock back */
 
1486
            if ((err = dav_fs_save_lock_record(lockdb, ip->key, ref_dp,
 
1487
                                               ref_ip)) != NULL) {
 
1488
                /* ### push a higher-level desc? */
 
1489
                return err;
 
1490
            }
 
1491
        }
 
1492
    }
 
1493
 
 
1494
    return NULL;
 
1495
}
 
1496
 
 
1497
 
 
1498
const dav_hooks_locks dav_hooks_locks_fs =
 
1499
{
 
1500
    dav_fs_get_supportedlock,
 
1501
    dav_fs_parse_locktoken,
 
1502
    dav_fs_format_locktoken,
 
1503
    dav_fs_compare_locktoken,
 
1504
    dav_fs_open_lockdb,
 
1505
    dav_fs_close_lockdb,
 
1506
    dav_fs_remove_locknull_state,
 
1507
    dav_fs_create_lock,
 
1508
    dav_fs_get_locks,
 
1509
    dav_fs_find_lock,
 
1510
    dav_fs_has_locks,
 
1511
    dav_fs_append_locks,
 
1512
    dav_fs_remove_lock,
 
1513
    dav_fs_refresh_locks,
 
1514
    NULL, /* lookup_resource */
 
1515
 
 
1516
    NULL /* ctx */
 
1517
};