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