]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_lock.c
9142e302fc9d8a9cbeb7c5a12ff593bdad91c3a4
[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 /******************************************************************************
497  * Public functions
498  ******************************************************************************/
499
500 int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork)
501 {
502     struct flock lock;
503     struct ad_fd *adf;
504     adf_lock_t *adflock;
505     int oldlock;
506     int i;
507     int type;  
508
509     LOG(log_debug, logtype_default, "ad_lock(\"%s\", %s, %s, off: %jd, len: %jd): BEGIN",
510         ad->ad_m_name ? ad->ad_m_name : "???",
511         eid == ADEID_DFORK ? "data" : "reso",
512         locktypetostr(locktype), (intmax_t)off, (intmax_t)len);
513
514     if ((locktype & ADLOCK_FILELOCK) && (len != 1))
515         /* safety check */
516         return -1;
517
518     lock.l_start = off;
519     type = locktype;
520
521     if (eid == ADEID_DFORK) {
522         adf = &ad->ad_data_fork;
523         if ((ad->ad_vers == AD_VERSION2) && (type & ADLOCK_FILELOCK)) {
524             if (ad_meta_fileno(ad) != -1) { /* META */
525                 adf = ad->ad_mdp;
526                 lock.l_start = df2off(off);
527             }
528         }
529     } else { /* rfork */
530         switch (ad->ad_vers) {
531         case AD_VERSION2:
532             if (ad_meta_fileno(ad) == -1 || ad_reso_fileno(ad) == -1) {
533                 /* there's no meta data. return a lock error 
534                  * otherwise if a second process is able to create it
535                  * locks are a mess. */
536                 errno = EACCES;
537                 return -1;
538             }
539             if (type & ADLOCK_FILELOCK) {
540                 adf = ad->ad_mdp;                       /* either resource or meta data (set in ad_open) */
541                 lock.l_start = hf2off(off);
542             } else {
543                 /* we really want the resource fork it's a byte lock */
544                 adf = &ad->ad_resource_fork;
545                 lock.l_start += ad_getentryoff(ad, eid);
546             }
547             break;
548
549         case AD_VERSION_EA:
550             if (type & ADLOCK_FILELOCK) {
551                 lock.l_start = rf2off(off);
552             } else {
553                 /* it's a byterange lock on the rsrcfork -> discard it */
554                 return 0;
555             }
556             adf = &ad->ad_data_fork;
557             break;
558
559         default:
560             return -1;
561         }
562     }
563
564     /* NOTE: we can't write lock a read-only file. on those, we just
565      * make sure that we have a read lock set. that way, we at least prevent
566      * someone else from really setting a deny read/write on the file. 
567      */
568     if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
569         type = (type & ~ADLOCK_WR) | ADLOCK_RD;
570     }
571   
572     lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
573     lock.l_whence = SEEK_SET;
574     lock.l_len = len;
575
576     /* byte_lock(len=-1) lock whole file */
577     if (len == BYTELOCK_MAX) {
578         lock.l_len -= lock.l_start; /* otherwise  EOVERFLOW error */
579     }
580
581     /* see if it's locked by another fork. 
582      * NOTE: this guarantees that any existing locks must be at most
583      * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
584      * guaranteed to be ORable. */
585     if (adf_findxlock(adf, fork, ADLOCK_WR | 
586                       ((type & ADLOCK_WR) ? ADLOCK_RD : 0), 
587                       lock.l_start, lock.l_len) > -1) {
588         errno = EACCES;
589         return -1;
590     }
591   
592     /* look for any existing lock that we may have */
593     i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len);
594     adflock = (i < 0) ? NULL : adf->adf_lock + i;
595
596     /* here's what we check for:
597        1) we're trying to re-lock a lock, but we didn't specify an update.
598        2) we're trying to free only part of a lock. 
599        3) we're trying to free a non-existent lock. */
600     if ( (!adflock && (lock.l_type == F_UNLCK))
601          ||
602          (adflock
603           && !(type & ADLOCK_UPGRADE)
604           && ((lock.l_type != F_UNLCK)
605               || (adflock->lock.l_start != lock.l_start)
606               || (adflock->lock.l_len != lock.l_len) ))
607         ) {
608         errno = EINVAL;
609         return -1;
610     }
611
612
613     /* now, update our list of locks */
614     /* clear the lock */
615     if (lock.l_type == F_UNLCK) { 
616         adf_freelock(adf, i);
617         return 0;
618     }
619
620     /* attempt to lock the file. */
621     if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0) 
622         return -1;
623
624     /* we upgraded this lock. */
625     if (adflock && (type & ADLOCK_UPGRADE)) {
626         memcpy(&adflock->lock, &lock, sizeof(lock));
627         return 0;
628     } 
629
630     /* it wasn't an upgrade */
631     oldlock = -1;
632     if (lock.l_type == F_RDLCK) {
633         oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len);
634     } 
635     
636     /* no more space. this will also happen if lockmax == lockcount == 0 */
637     if (adf->adf_lockmax == adf->adf_lockcount) { 
638         adf_lock_t *tmp = (adf_lock_t *) 
639             realloc(adf->adf_lock, sizeof(adf_lock_t)*
640                     (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
641         if (!tmp)
642             goto fcntl_lock_err;
643         adf->adf_lock = tmp;
644         adf->adf_lockmax += ARRAY_BLOCK_SIZE;
645     } 
646     adflock = adf->adf_lock + adf->adf_lockcount;
647
648     /* fill in fields */
649     memcpy(&adflock->lock, &lock, sizeof(lock));
650     adflock->user = fork;
651     if (oldlock > -1) {
652         adflock->refcount = (adf->adf_lock + oldlock)->refcount;
653     } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
654         goto fcntl_lock_err;
655     }
656   
657     (*adflock->refcount)++;
658     adf->adf_lockcount++;
659     return 0;
660
661 fcntl_lock_err:
662     lock.l_type = F_UNLCK;
663     set_lock(adf->adf_fd, F_SETLK, &lock);
664     return -1;
665 }
666
667 /* -------------------------
668 */
669 int ad_tmplock(struct adouble *ad, const uint32_t eid, const int locktype,
670                const off_t off, const off_t len, const int fork)
671 {
672     struct flock lock;
673     struct ad_fd *adf;
674     int err;
675     int type;  
676
677     lock.l_start = off;
678     type = locktype;
679     if (eid == ADEID_DFORK) {
680         adf = &ad->ad_data_fork;
681     } else {
682         /* FIXME META */
683         adf = &ad->ad_resource_fork;
684         if (adf->adf_fd == -1) {
685             /* there's no resource fork. return success */
686             return 0;
687         }
688         /* if ADLOCK_FILELOCK we want a lock from offset 0
689          * it's used when deleting a file:
690          * in open we put read locks on meta datas
691          * in delete a write locks on the whole file
692          * so if the file is open by somebody else it fails
693          */
694         if (!(type & ADLOCK_FILELOCK))
695             lock.l_start += ad_getentryoff(ad, eid);
696     }
697
698     if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
699         type = (type & ~ADLOCK_WR) | ADLOCK_RD;
700     }
701   
702     lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
703     lock.l_whence = SEEK_SET;
704     lock.l_len = len;
705
706     /* see if it's locked by another fork. */
707     if (fork && adf_findxlock(adf, fork,
708                               ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0), 
709                               lock.l_start, lock.l_len) > -1) {
710         errno = EACCES;
711         return -1;
712     }
713
714     /* okay, we might have ranges byte-locked. we need to make sure that
715      * we restore the appropriate ranges once we're done. so, we check
716      * for overlap on an unlock and relock. 
717      * XXX: in the future, all the byte locks will be sorted and contiguous.
718      *      we just want to upgrade all the locks and then downgrade them
719      *      here. */
720     err = set_lock(adf->adf_fd, F_SETLK, &lock);
721     if (!err && (lock.l_type == F_UNLCK))
722         adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
723
724     return err;
725 }
726
727 /* --------------------- */
728 void ad_unlock(struct adouble *ad, const int fork)
729 {
730     if (ad_data_fileno(ad) != -1) {
731         adf_unlock(&ad->ad_data_fork, fork);
732     }
733     if (ad_reso_fileno(ad) != -1) {
734         adf_unlock(&ad->ad_resource_fork, fork);
735     }
736 }
737
738 /*!
739  * Test for a share mode lock
740  *
741  * @param ad      (rw) handle
742  * @param eid     (r)  datafork or ressource fork
743  * @param off     (r)  sharemode lock to test
744  *
745  * @returns           1 if there's an existing lock, 0 if there's no lock,
746  *                    -1 in case any error occured
747  */
748 int ad_testlock(struct adouble *ad, int eid, const off_t off)
749 {
750     switch (ad->ad_vers) {
751     case AD_VERSION2:
752         return ad_testlock_v2(ad, eid, off);
753     case AD_VERSION_EA:
754         return ad_testlock_ea(ad, eid, off);
755     default:
756         return -1;
757     }
758 }
759
760 /*!
761  * Return if a file is open by another process.
762  *
763  * Optimized for the common case:
764  * - there's no locks held by another process (clients)
765  * - or we already know the answer and don't need to test (attrbits)
766  *
767  * @param ad          (rw) handle
768  * @param attrbits    (r)  forks opened by us
769  * @returns                bitflags ATTRBIT_DOPEN | ATTRBIT_ROPEN if
770  *                         other process has fork of file opened 
771  */
772 uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
773 {
774     switch (ad->ad_vers) {
775     case AD_VERSION2:
776         return ad_openforks_v2(ad, attrbits);
777     case AD_VERSION_EA:
778         return ad_openforks_ea(ad, attrbits);
779     default:
780         return -1;
781     }
782 }