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