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