]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_lock.c
More logging
[netatalk.git] / libatalk / adouble / ad_lock.c
1 /* 
2  * Copyright (c) 1998,1999 Adrian Sun (asun@zoology.washington.edu)
3  * All Rights Reserved. See COPYRIGHT for more information.
4  *
5  * Because fcntl locks are
6  * process-oriented, we need to keep around a list of file descriptors
7  * that refer to the same file.
8  *
9  * TODO: fix the race when reading/writing.
10  *       keep a pool of both locks and reference counters around so that
11  *       we can save on mallocs. we should also use a tree to keep things
12  *       sorted.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <atalk/adouble.h>
20 #include <atalk/logger.h>
21 #include <atalk/compat.h>
22 #include <atalk/errchk.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <inttypes.h>
28
29 #include <string.h>
30
31 #include "ad_lock.h"
32
33 static const char *shmdstrfromoff(off_t off)
34 {
35     switch (off) {
36     case AD_FILELOCK_OPEN_WR:
37         return "OPEN_WR_DATA";
38     case AD_FILELOCK_OPEN_RD:
39         return "OPEN_RD_DATA";
40     case AD_FILELOCK_RSRC_OPEN_WR:
41         return "OPEN_WR_RSRC";
42     case AD_FILELOCK_RSRC_OPEN_RD:
43         return "OPEN_RD_RSRC";
44     case AD_FILELOCK_DENY_WR:
45         return "DENY_WR_DATA";
46     case AD_FILELOCK_DENY_RD:
47         return "DENY_RD_DATA";
48     case AD_FILELOCK_RSRC_DENY_WR:
49         return "DENY_WR_RSRC";
50     case AD_FILELOCK_RSRC_DENY_RD:
51         return "DENY_RD_RSRC";
52     case AD_FILELOCK_OPEN_NONE:
53         return "OPEN_NONE_DATA";
54     case AD_FILELOCK_RSRC_OPEN_NONE:
55         return "OPEN_NONE_RSRC";
56     default:
57         return "-";
58     }
59 }
60
61 /* ----------------------- */
62 static int set_lock(int fd, int cmd,  struct flock *lock)
63 {
64     EC_INIT;
65
66     LOG(log_debug, logtype_default, "set_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
67         cmd == F_SETLK ? "F_SETLK" : "F_GETLK",
68         lock->l_type == F_RDLCK ? "F_RDLCK" : lock->l_type == F_WRLCK ? "F_WRLCK" : "F_UNLCK",
69         (intmax_t)lock->l_start,
70         shmdstrfromoff(lock->l_start),
71         (intmax_t)lock->l_len);
72
73     if (fd == -2) {
74         /* We assign fd = -2 for symlinks -> do nothing */
75         if (cmd == F_GETLK)
76             lock->l_type = F_UNLCK;
77         return 0;
78     }
79
80     EC_NEG1_LOGSTR( fcntl(fd, cmd, lock),
81                     "set_lock: %s", strerror(errno));
82
83 EC_CLEANUP:
84     EC_EXIT;
85 }
86
87 /* ----------------------- */
88 static int XLATE_FCNTL_LOCK(int type) 
89 {
90     switch(type) {
91     case ADLOCK_RD:
92         return F_RDLCK;
93     case ADLOCK_WR:
94          return F_WRLCK;
95     case ADLOCK_CLR:
96          return F_UNLCK;
97     }
98     return -1;
99 }
100
101 /* ----------------------- */
102 static int OVERLAP(off_t a, off_t alen, off_t b, off_t blen) 
103 {
104     return (!alen && a <= b) || 
105         (!blen && b <= a) || 
106         ( (a + alen > b) && (b + blen > a) );
107 }
108
109 /* allocation for lock regions. we allocate aggressively and shrink
110  * only in large chunks. */
111 #define ARRAY_BLOCK_SIZE 10 
112 #define ARRAY_FREE_DELTA 100
113
114 /* remove a lock and compact space if necessary */
115 static void adf_freelock(struct ad_fd *ad, const int i)
116 {
117     adf_lock_t *lock = ad->adf_lock + i;
118
119     if (--(*lock->refcount) < 1) {
120         free(lock->refcount); 
121     lock->lock.l_type = F_UNLCK;
122     set_lock(ad->adf_fd, F_SETLK, &lock->lock); /* unlock */
123     }
124
125     ad->adf_lockcount--;
126
127     /* move another lock into the empty space */
128     if (i < ad->adf_lockcount) {
129         memcpy(lock, lock + ad->adf_lockcount - i, sizeof(adf_lock_t));
130     }
131
132     /* free extra cruft if we go past a boundary. we always want to
133      * keep at least some stuff around for allocations. this wastes
134      * a bit of space to save time on reallocations. */
135     if ((ad->adf_lockmax > ARRAY_FREE_DELTA) &&
136         (ad->adf_lockcount + ARRAY_FREE_DELTA < ad->adf_lockmax)) {
137             struct adf_lock_t *tmp;
138
139             tmp = (struct adf_lock_t *) 
140                     realloc(ad->adf_lock, sizeof(adf_lock_t)*
141                             (ad->adf_lockcount + ARRAY_FREE_DELTA));
142             if (tmp) {
143                 ad->adf_lock = tmp;
144                 ad->adf_lockmax = ad->adf_lockcount + ARRAY_FREE_DELTA;
145             }
146     }
147 }
148
149
150 /* this needs to deal with the following cases:
151  * 1) free all UNIX byterange lock from any fork
152  * 2) free all locks of the requested fork
153  *
154  * i converted to using arrays of locks. everytime a lock
155  * gets removed, we shift all of the locks down.
156  */
157 static void adf_unlock(struct ad_fd *ad, const int fork)
158 {
159     adf_lock_t *lock = ad->adf_lock;
160     int i;
161
162     for (i = 0; i < ad->adf_lockcount; i++) {
163         if (lock[i].lock.l_start < AD_FILELOCK_BASE
164             || lock[i].user == fork) {
165             /* we're really going to delete this lock. note: read locks
166                are the only ones that allow refcounts > 1 */
167             adf_freelock(ad, i);
168             /* we shifted things down, so we need to backtrack */
169             i--; 
170             /* unlikely but realloc may have change adf_lock */
171             lock = ad->adf_lock;       
172         }
173     }
174 }
175
176 /* relock any byte lock that overlaps off/len. unlock everything
177  * else. */
178 static void adf_relockrange(struct ad_fd *ad, int fd, off_t off, off_t len)
179 {
180     adf_lock_t *lock = ad->adf_lock;
181     int i;
182     
183     for (i = 0; i < ad->adf_lockcount; i++) {
184         if (OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) 
185             set_lock(fd, F_SETLK, &lock[i].lock);
186     }
187 }
188
189
190 /* find a byte lock that overlaps off/len for a particular open fork */
191 static int adf_findlock(struct ad_fd *ad,
192                         const int fork, const int type,
193                         const off_t off,
194                         const off_t len)
195 {
196   adf_lock_t *lock = ad->adf_lock;
197   int i;
198   
199   for (i = 0; i < ad->adf_lockcount; i++) {
200     if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
201         ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
202         (lock[i].user == fork) && 
203         OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) {
204       return i;
205     }
206   }
207   return -1;
208 }
209
210
211 /* search other fork lock lists */
212 static int adf_findxlock(struct ad_fd *ad, 
213                          const int fork, const int type,
214                          const off_t off,
215                          const off_t len)
216 {
217     adf_lock_t *lock = ad->adf_lock;
218     int i;
219   
220     for (i = 0; i < ad->adf_lockcount; i++) {
221         if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK))
222              ||
223              ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK)))
224             &&
225             (lock[i].user != fork)
226             && 
227             OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) 
228             return i;
229     } 
230     return -1;
231 }
232
233 /* okay, this needs to do the following:
234  * 1) check current list of locks. error on conflict.
235  * 2) apply the lock. error on conflict with another process.
236  * 3) update the list of locks this file has.
237  * 
238  * NOTE: this treats synchronization locks a little differently. we
239  *       do the following things for those:
240  *       1) if the header file exists, all the locks go in the beginning
241  *          of that.
242  *       2) if the header file doesn't exist, we stick the locks
243  *          in the locations specified by AD_FILELOCK_RD/WR.
244  */
245 #define LOCK_DATA_WR (0)
246 #define LOCK_DATA_RD (1)
247 #define LOCK_RSRC_WR (2)
248 #define LOCK_RSRC_RD (3)
249
250 #define LOCK_RSRC_DRD (4)
251 #define LOCK_RSRC_DWR (5)
252 #define LOCK_DATA_DRD (6)
253 #define LOCK_DATA_DWR (7)
254
255 #define LOCK_RSRC_NONE (8)
256 #define LOCK_DATA_NONE (9)
257
258 /* -------------- 
259         translate a data fork lock to an offset
260 */
261
262 static off_t df2off(off_t off)
263 {
264     off_t start = off;
265         if (off == AD_FILELOCK_OPEN_WR)
266                 start = LOCK_DATA_WR;
267         else if (off == AD_FILELOCK_OPEN_RD)
268                 start = LOCK_DATA_RD;
269     else if (off == AD_FILELOCK_DENY_RD)
270                 start = LOCK_DATA_DRD;
271         else if (off == AD_FILELOCK_DENY_WR)
272                 start = LOCK_DATA_DWR;
273         else if (off == AD_FILELOCK_OPEN_NONE)
274                 start = LOCK_DATA_NONE;
275         return start;
276 }
277
278 /* -------------- 
279         translate a resource fork lock to an offset
280 */
281
282 static off_t hf2off(off_t off)
283 {
284     off_t start = off;
285         if (off == AD_FILELOCK_OPEN_WR)
286                 start = LOCK_RSRC_WR;
287         else if (off == AD_FILELOCK_OPEN_RD)
288                 start = LOCK_RSRC_RD;
289     else if (off == AD_FILELOCK_DENY_RD)
290                 start = LOCK_RSRC_DRD;
291         else if (off == AD_FILELOCK_DENY_WR)
292                 start = LOCK_RSRC_DWR;
293         else if (off == AD_FILELOCK_OPEN_NONE)
294                 start = LOCK_RSRC_NONE;
295         return start;
296 }
297
298 /* -------------- 
299         translate a resource fork lock to an offset
300 */
301 static off_t rf2off(off_t off)
302 {
303     off_t start = off;
304         if (off == AD_FILELOCK_OPEN_WR)
305                 start = AD_FILELOCK_RSRC_OPEN_WR;
306         else if (off == AD_FILELOCK_OPEN_RD)
307                 start = AD_FILELOCK_RSRC_OPEN_RD;
308     else if (off == AD_FILELOCK_DENY_RD)
309                 start = AD_FILELOCK_RSRC_DENY_RD;
310         else if (off == AD_FILELOCK_DENY_WR)
311                 start = AD_FILELOCK_RSRC_DENY_WR;
312         else if (off == AD_FILELOCK_OPEN_NONE)
313                 start = AD_FILELOCK_RSRC_OPEN_NONE;
314         return start;
315 }
316
317 /*!
318  * Test a lock
319  *
320  * (1) Test against our own locks array
321  * (2) Test fcntl lock, locks from other processes
322  *
323  * @param adf     (r) handle
324  * @param off     (r) offset
325  * @param len     (r) lenght
326  *
327  * @returns           1 if there's an existing lock, 0 if there's no lock,
328  *                    -1 in case any error occured
329  */
330 static int testlock(const struct ad_fd *adf, off_t off, off_t len)
331 {
332     struct flock lock;
333     adf_lock_t *plock;
334     int i;
335
336     lock.l_start = off;
337
338     plock = adf->adf_lock;
339     lock.l_whence = SEEK_SET;
340     lock.l_len = len;
341
342     /* (1) Do we have a lock ? */
343     for (i = 0; i < adf->adf_lockcount; i++) {
344         if (OVERLAP(lock.l_start, 1, plock[i].lock.l_start, plock[i].lock.l_len)) 
345             return 1;
346     }
347
348     /* (2) Does another process have a lock? */
349     lock.l_type = (adf->adf_flags & O_RDWR) ? F_WRLCK : F_RDLCK;
350
351     if (set_lock(adf->adf_fd, F_GETLK, &lock) < 0) {
352         /* is that kind of error possible ?*/
353         return (errno == EACCES || errno == EAGAIN) ? 1 : -1;
354     }
355   
356     if (lock.l_type == F_UNLCK) {
357         return 0;
358     }
359     return 1;
360 }
361
362 static uint16_t ad_openforks_v2(struct adouble *ad, uint16_t attrbits)
363 {
364   uint16_t ret = 0;
365   struct ad_fd *adf;
366   off_t off;
367
368   if (!(attrbits & (ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
369       off_t len;
370       /* XXX know the locks layout: 
371          AD_FILELOCK_OPEN_WR is first 
372          and use it for merging requests
373       */
374       if (ad_meta_fileno(ad) != -1) {
375           /* there's a resource fork test the four bytes for
376            * data RW/RD and fork RW/RD locks in one request
377           */
378           adf = ad->ad_mdp;
379           off = LOCK_DATA_WR;
380           len = 4;
381       }
382       else {
383           /* no resource fork, only data RD/RW may exist */
384           adf = &ad->ad_data_fork;
385           off = AD_FILELOCK_OPEN_WR;
386           len = 2;
387       }
388       if (!testlock(adf, off, len))
389           return ret;
390   }
391   /* either there's a lock or we already know one 
392      fork is open
393   */
394   if (!(attrbits & ATTRBIT_DOPEN)) {
395       if (ad_meta_fileno(ad) != -1) {
396           adf = ad->ad_mdp;
397           off = LOCK_DATA_WR;
398       }
399       else {
400           adf = &ad->ad_data_fork;
401           off = AD_FILELOCK_OPEN_WR;
402       }
403       ret = testlock(adf, off, 2) > 0? ATTRBIT_DOPEN : 0;
404   }
405
406   if (!(attrbits & ATTRBIT_ROPEN)) {
407       if (ad_meta_fileno(ad) != -1) {
408           adf = ad->ad_mdp;
409           off = LOCK_RSRC_WR;
410           ret |= testlock(adf, off, 2) > 0? ATTRBIT_ROPEN : 0;
411       }
412   }
413
414   return ret;
415 }
416
417 /* test for sharemode locks, adouble:ea stores them on the datafork */
418 static uint16_t ad_openforks_ea(struct adouble *ad, uint16_t attrbits)
419 {
420     uint16_t ret = 0;
421     struct ad_fd *adf;
422     off_t off;
423     off_t len;
424
425     if (ad_data_fileno(ad) == -1)
426         return 0;
427
428     if (!(attrbits & (ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
429         /* Test all 4 locks at once */
430         off = AD_FILELOCK_OPEN_WR;
431         len = 4;
432         if (testlock(&ad->ad_data_fork, off, len) == 0)
433             return 0;
434     }
435
436     /* either there's a lock or we already know one fork is open */
437
438     if (!(attrbits & ATTRBIT_DOPEN)) {
439         off = AD_FILELOCK_OPEN_WR;
440         ret = testlock(&ad->ad_data_fork, off, 2) > 0 ? ATTRBIT_DOPEN : 0;
441     }
442
443     if (!(attrbits & ATTRBIT_ROPEN)) {
444         off = AD_FILELOCK_RSRC_OPEN_WR;
445         ret |= testlock(&ad->ad_data_fork, off, 2) > 0? ATTRBIT_ROPEN : 0;
446     }
447
448     return ret;
449 }
450
451 static int ad_testlock_v2(struct adouble *ad, int eid, const off_t off)
452 {
453     struct ad_fd *adf;
454     off_t      lock_offset;
455
456     lock_offset = off;
457     if (eid == ADEID_DFORK) {
458         adf = &ad->ad_data_fork;
459         if (ad_meta_fileno(ad) != -1) {
460             adf = ad->ad_mdp;
461             lock_offset = df2off(off);
462         }
463     } else { /* rfork */
464         if (ad_meta_fileno(ad) == -1) {
465             /* there's no resource fork. return no lock */
466             return 0;
467         }
468         adf = ad->ad_mdp;
469         lock_offset = hf2off(off);
470     }
471     return testlock(adf, lock_offset, 1);
472 }
473
474 static int ad_testlock_ea(struct adouble *ad, int eid, const off_t off)
475 {
476     off_t      lock_offset;
477
478     if (eid == ADEID_DFORK) {
479         lock_offset = off;
480     } else { /* rfork */
481         lock_offset = rf2off(off);
482     }
483     return testlock(&ad->ad_data_fork, lock_offset, 1);
484 }
485
486 #define LTYPE2STRBUFSIZ 128
487 static const char *locktypetostr(int type)
488 {
489     int first = 1;
490     static char buf[LTYPE2STRBUFSIZ];
491
492     buf[0] = 0;
493
494     if (type == 0) {
495         strlcat(buf, "CLR", LTYPE2STRBUFSIZ);
496         first = 0;
497         return buf;
498     }
499     if (type & ADLOCK_RD) {
500         if (!first)
501             strlcat(buf, "|", LTYPE2STRBUFSIZ);
502         strlcat(buf, "RD", LTYPE2STRBUFSIZ);
503         first = 0;
504     }
505     if (type & ADLOCK_WR) {
506         if (!first)
507             strlcat(buf, "|", LTYPE2STRBUFSIZ);
508         strlcat(buf, "WR", LTYPE2STRBUFSIZ);
509         first = 0;
510     }
511     if (type & ADLOCK_UPGRADE) {
512         if (!first)
513             strlcat(buf, "|", LTYPE2STRBUFSIZ);
514         strlcat(buf, "UPG", LTYPE2STRBUFSIZ);
515         first = 0;
516     }
517     if (type & ADLOCK_FILELOCK) {
518         if (!first)
519             strlcat(buf, "|", LTYPE2STRBUFSIZ);
520         strlcat(buf, "FILELOCK", LTYPE2STRBUFSIZ);
521         first = 0;
522     }
523
524     return buf;
525 }
526
527 /******************************************************************************
528  * Public functions
529  ******************************************************************************/
530
531 int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork)
532 {
533     struct flock lock;
534     struct ad_fd *adf;
535     adf_lock_t *adflock;
536     int oldlock;
537     int i;
538     int type;  
539     int ret = 0, fcntl_lock_err = 0;
540
541     LOG(log_debug, logtype_default, "ad_lock(\"%s\", %s, %s, off: %jd (%s), len: %jd): BEGIN",
542         ad->ad_m_name ? ad->ad_m_name : "???",
543         eid == ADEID_DFORK ? "data" : "reso",
544         locktypetostr(locktype),
545         (intmax_t)off,
546         shmdstrfromoff(off),
547         (intmax_t)len);
548
549     if ((locktype & ADLOCK_FILELOCK) && (len != 1)) {
550         /* safety check */
551         ret = -1;
552         goto exit;
553     }
554
555     lock.l_start = off;
556     type = locktype;
557
558     if (eid == ADEID_DFORK) {
559         adf = &ad->ad_data_fork;
560         if ((ad->ad_vers == AD_VERSION2) && (type & ADLOCK_FILELOCK)) {
561             if (ad_meta_fileno(ad) != -1) { /* META */
562                 adf = ad->ad_mdp;
563                 lock.l_start = df2off(off);
564             }
565         }
566     } else { /* rfork */
567         switch (ad->ad_vers) {
568         case AD_VERSION2:
569             if (ad_meta_fileno(ad) == -1 || ad_reso_fileno(ad) == -1) {
570                 /* there's no meta data. return a lock error 
571                  * otherwise if a second process is able to create it
572                  * locks are a mess. */
573                 errno = EACCES;
574                 ret= -1;
575                 goto exit;
576             }
577             if (type & ADLOCK_FILELOCK) {
578                 adf = ad->ad_mdp;                       /* either resource or meta data (set in ad_open) */
579                 lock.l_start = hf2off(off);
580             } else {
581                 /* we really want the resource fork it's a byte lock */
582                 adf = &ad->ad_resource_fork;
583                 lock.l_start += ad_getentryoff(ad, eid);
584             }
585             break;
586
587         case AD_VERSION_EA:
588             if (type & ADLOCK_FILELOCK) {
589                 adf = &ad->ad_data_fork;
590                 lock.l_start = rf2off(off);
591             } else {
592                 adf = ad->ad_rfp;
593 #if HAVE_EAFD
594                 lock.l_start = off;
595 #else
596                 lock.l_start= ADEDOFF_RFORK_OSX + off;
597 #endif
598             }
599             break;
600
601         default:
602             ret = -1;
603             goto exit;
604         }
605     }
606
607     /* NOTE: we can't write lock a read-only file. on those, we just
608      * make sure that we have a read lock set. that way, we at least prevent
609      * someone else from really setting a deny read/write on the file. 
610      */
611     if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
612         type = (type & ~ADLOCK_WR) | ADLOCK_RD;
613     }
614   
615     lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
616     lock.l_whence = SEEK_SET;
617     lock.l_len = len;
618
619     /* byte_lock(len=-1) lock whole file */
620     if (len == BYTELOCK_MAX) {
621         lock.l_len -= lock.l_start; /* otherwise  EOVERFLOW error */
622     }
623
624     /* see if it's locked by another fork. 
625      * NOTE: this guarantees that any existing locks must be at most
626      * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
627      * guaranteed to be ORable. */
628     if (adf_findxlock(adf, fork, ADLOCK_WR | 
629                       ((type & ADLOCK_WR) ? ADLOCK_RD : 0), 
630                       lock.l_start, lock.l_len) > -1) {
631         errno = EACCES;
632         ret = -1;
633         goto exit;
634     }
635   
636     /* look for any existing lock that we may have */
637     i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len);
638     adflock = (i < 0) ? NULL : adf->adf_lock + i;
639
640     /* here's what we check for:
641        1) we're trying to re-lock a lock, but we didn't specify an update.
642        2) we're trying to free only part of a lock. 
643        3) we're trying to free a non-existent lock. */
644     if ( (!adflock && (lock.l_type == F_UNLCK))
645          ||
646          (adflock
647           && !(type & ADLOCK_UPGRADE)
648           && ((lock.l_type != F_UNLCK)
649               || (adflock->lock.l_start != lock.l_start)
650               || (adflock->lock.l_len != lock.l_len) ))
651         ) {
652         errno = EINVAL;
653         ret = -1;
654         goto exit;
655     }
656
657
658     /* now, update our list of locks */
659     /* clear the lock */
660     if (lock.l_type == F_UNLCK) { 
661         adf_freelock(adf, i);
662         goto exit;
663     }
664
665     /* attempt to lock the file. */
666     if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0) {
667         ret = -1;
668         goto exit;
669     }
670
671     /* we upgraded this lock. */
672     if (adflock && (type & ADLOCK_UPGRADE)) {
673         memcpy(&adflock->lock, &lock, sizeof(lock));
674         goto exit;
675     } 
676
677     /* it wasn't an upgrade */
678     oldlock = -1;
679     if (lock.l_type == F_RDLCK) {
680         oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len);
681     } 
682     
683     /* no more space. this will also happen if lockmax == lockcount == 0 */
684     if (adf->adf_lockmax == adf->adf_lockcount) { 
685         adf_lock_t *tmp = (adf_lock_t *) 
686             realloc(adf->adf_lock, sizeof(adf_lock_t)*
687                     (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
688         if (!tmp) {
689             ret = fcntl_lock_err = -1;
690             goto exit;
691         }
692         adf->adf_lock = tmp;
693         adf->adf_lockmax += ARRAY_BLOCK_SIZE;
694     } 
695     adflock = adf->adf_lock + adf->adf_lockcount;
696
697     /* fill in fields */
698     memcpy(&adflock->lock, &lock, sizeof(lock));
699     adflock->user = fork;
700     if (oldlock > -1) {
701         adflock->refcount = (adf->adf_lock + oldlock)->refcount;
702     } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
703         ret = fcntl_lock_err = 1;
704         goto exit;
705     }
706   
707     (*adflock->refcount)++;
708     adf->adf_lockcount++;
709
710 exit:
711     if (ret != 0) {
712         if (fcntl_lock_err != 0) {
713             lock.l_type = F_UNLCK;
714             set_lock(adf->adf_fd, F_SETLK, &lock);
715         }
716     }
717     LOG(log_debug, logtype_default, "ad_lock: END: %d", ret);
718     return ret;
719 }
720
721 int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork)
722 {
723     struct flock lock;
724     struct ad_fd *adf;
725     int err;
726     int type;  
727
728     LOG(log_debug, logtype_default, "ad_tmplock(\"%s\", %s, %s, off: %jd (%s), len: %jd): BEGIN",
729         ad->ad_m_name ? ad->ad_m_name : "???",
730         eid == ADEID_DFORK ? "data" : "reso",
731         locktypetostr(locktype),
732         (intmax_t)off,
733         shmdstrfromoff(off),
734         (intmax_t)len);
735
736     lock.l_start = off;
737     type = locktype;
738     if (eid == ADEID_DFORK) {
739         adf = &ad->ad_data_fork;
740     } else {
741         /* FIXME META */
742         adf = &ad->ad_resource_fork;
743         if (adf->adf_fd == -1) {
744             /* there's no resource fork. return success */
745             err = 0;
746             goto exit;
747         }
748         /* if ADLOCK_FILELOCK we want a lock from offset 0
749          * it's used when deleting a file:
750          * in open we put read locks on meta datas
751          * in delete a write locks on the whole file
752          * so if the file is open by somebody else it fails
753          */
754         if (!(type & ADLOCK_FILELOCK))
755             lock.l_start += ad_getentryoff(ad, eid);
756     }
757
758     if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
759         type = (type & ~ADLOCK_WR) | ADLOCK_RD;
760     }
761   
762     lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
763     lock.l_whence = SEEK_SET;
764     lock.l_len = len;
765
766     /* see if it's locked by another fork. */
767     if (fork && adf_findxlock(adf, fork,
768                               ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0), 
769                               lock.l_start, lock.l_len) > -1) {
770         errno = EACCES;
771         err = -1;
772         goto exit;
773     }
774
775     /* okay, we might have ranges byte-locked. we need to make sure that
776      * we restore the appropriate ranges once we're done. so, we check
777      * for overlap on an unlock and relock. 
778      * XXX: in the future, all the byte locks will be sorted and contiguous.
779      *      we just want to upgrade all the locks and then downgrade them
780      *      here. */
781     err = set_lock(adf->adf_fd, F_SETLK, &lock);
782     if (!err && (lock.l_type == F_UNLCK))
783         adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
784
785 exit:
786     LOG(log_debug, logtype_default, "ad_tmplock: END: %d", err);
787     return err;
788 }
789
790 /* --------------------- */
791 void ad_unlock(struct adouble *ad, const int fork)
792 {
793     LOG(log_debug, logtype_default, "ad_unlock(\"%s\"): BEGIN", ad->ad_m_name ? ad->ad_m_name : "???");
794
795     if (ad_data_fileno(ad) != -1) {
796         adf_unlock(&ad->ad_data_fork, fork);
797     }
798     if (ad_reso_fileno(ad) != -1) {
799         adf_unlock(&ad->ad_resource_fork, fork);
800     }
801
802     LOG(log_debug, logtype_default, "ad_unlock(\"%s\"): END", ad->ad_m_name ? ad->ad_m_name : "???");
803 }
804
805 /*!
806  * Test for a share mode lock
807  *
808  * @param ad      (rw) handle
809  * @param eid     (r)  datafork or ressource fork
810  * @param off     (r)  sharemode lock to test
811  *
812  * @returns           1 if there's an existing lock, 0 if there's no lock,
813  *                    -1 in case any error occured
814  */
815 int ad_testlock(struct adouble *ad, int eid, const off_t off)
816 {
817     int ret = 0;
818
819     LOG(log_debug, logtype_default, "ad_testlock(\"%s\", %s, off: %jd (%s): BEGIN",
820         ad->ad_m_name ? ad->ad_m_name : "???",
821         eid == ADEID_DFORK ? "data" : "reso",
822         (intmax_t)off,
823         shmdstrfromoff(off));
824
825     switch (ad->ad_vers) {
826     case AD_VERSION2:
827         ret = ad_testlock_v2(ad, eid, off);
828         break;
829     case AD_VERSION_EA:
830         ret = ad_testlock_ea(ad, eid, off);
831         break;
832     default:
833         ret = -1;
834         break;
835     }
836
837     LOG(log_debug, logtype_default, "ad_testlock: END: %d", ret);
838     return ret;
839 }
840
841 /*!
842  * Return if a file is open by another process.
843  *
844  * Optimized for the common case:
845  * - there's no locks held by another process (clients)
846  * - or we already know the answer and don't need to test (attrbits)
847  *
848  * @param ad          (rw) handle
849  * @param attrbits    (r)  forks opened by us
850  * @returns                bitflags ATTRBIT_DOPEN | ATTRBIT_ROPEN if
851  *                         other process has fork of file opened 
852  */
853 uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
854 {
855     switch (ad->ad_vers) {
856     case AD_VERSION2:
857         return ad_openforks_v2(ad, attrbits);
858     case AD_VERSION_EA:
859         return ad_openforks_ea(ad, attrbits);
860     default:
861         return -1;
862     }
863 }