]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
Fixes
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
3  * Copyright (c) 1990,1991 Regents of The University of Michigan.
4  * Copyright (c) 2010 Frank Lahm
5  *
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software and
9  * its documentation for any purpose and without fee is hereby granted,
10  * provided that the above copyright notice appears in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation, and that the name of The University
13  * of Michigan not be used in advertising or publicity pertaining to
14  * distribution of the software without specific, written prior
15  * permission. This software is supplied as is without expressed or
16  * implied warranties of any kind.
17  *
18  *  Research Systems Unix Group
19  *  The University of Michigan
20  *  c/o Mike Clark
21  *  535 W. William Street
22  *  Ann Arbor, Michigan
23  *  +1-313-763-0525
24  *  netatalk@itd.umich.edu
25  *
26  */
27
28 /*!
29  * @file
30  * Part of Netatalk's AppleDouble implementatation
31  * @sa include/atalk/adouble.h
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif /* HAVE_CONFIG_H */
37
38 #include <errno.h>
39 #include <sys/param.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include <arpa/inet.h>
44
45 #include <atalk/logger.h>
46 #include <atalk/adouble.h>
47 #include <atalk/util.h>
48 #include <atalk/ea.h>
49 #include <atalk/bstrlib.h>
50 #include <atalk/bstradd.h>
51 #include <atalk/compat.h>
52 #include <atalk/errchk.h>
53 #include <atalk/volume.h>
54
55 #include "ad_lock.h"
56
57 #define ADEDOFF_MAGIC        (0)
58 #define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
59 #define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
60 #define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
61
62 /* initial lengths of some of the fields */
63 #define ADEDLEN_INIT     0
64
65 /* i stick things in a slightly different order than their eid order in
66  * case i ever want to separate RootInfo behaviour from the rest of the
67  * stuff. */
68
69 /* ad:v2 */
70 #define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
71 #define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
72 #define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
73 #define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
74 #define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
75 #define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
76 #define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
77 #define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
78 #define ADEDOFF_PRIVDEV      (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
79 #define ADEDOFF_PRIVINO      (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
80 #define ADEDOFF_PRIVSYN      (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
81 #define ADEDOFF_PRIVID       (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
82 #define ADEDOFF_RFORK_V2     (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
83
84 /* ad:ea */
85 #define ADEDOFF_FINDERI_EA   (AD_HEADER_LEN + ADEID_NUM_EA * AD_ENTRY_LEN)
86 #define ADEDOFF_COMMENT_EA   (ADEDOFF_FINDERI_EA + ADEDLEN_FINDERI)
87 #define ADEDOFF_FILEDATESI_EA (ADEDOFF_COMMENT_EA + ADEDLEN_COMMENT)
88 #define ADEDOFF_AFPFILEI_EA  (ADEDOFF_FILEDATESI_EA + ADEDLEN_FILEDATESI)
89
90 /* this is to prevent changing timezones from causing problems with
91    localtime volumes. the screw-up is 30 years. we use a delta of 5 years */
92 #define TIMEWARP_DELTA 157680000
93
94 struct entry {
95     uint32_t id, offset, len;
96 };
97
98 /* --------------------------- */
99 static uid_t default_uid = -1;
100
101 /* Forward declarations */
102 static int ad_mkrf(const char *path);
103 static int ad_header_read(const char *path, struct adouble *ad, struct stat *hst);
104 static int ad_header_upgrade(struct adouble *ad, const char *name);
105
106 static int ad_mkrf_ea(const char *path);
107 static int ad_header_read_ea(const char *path, struct adouble *ad, struct stat *hst);
108 static int ad_header_upgrade_ea(struct adouble *ad, const char *name);
109 static int ad_reso_size(const char *path, int adflags, struct adouble *ad);
110 static int ad_mkrf_osx(const char *path);
111
112
113 static struct adouble_fops ad_adouble = {
114     &ad_path,
115     &ad_mkrf,
116     &ad_rebuild_adouble_header,
117     &ad_header_read,
118     &ad_header_upgrade,
119 };
120
121 static struct adouble_fops ad_adouble_ea = {
122 #ifdef HAVE_EAFD
123     &ad_path_ea,
124     &ad_mkrf_ea,
125 #else
126     &ad_path_osx,
127     &ad_mkrf_osx,
128 #endif
129     &ad_rebuild_adouble_header,
130     &ad_header_read_ea,
131     &ad_header_upgrade_ea,
132 };
133
134 static struct adouble_fops ad_osx = {
135     &ad_path_osx,
136     &ad_mkrf_osx,
137     &ad_rebuild_adouble_header,
138     &ad_header_read,
139     &ad_header_upgrade,
140 };
141
142 static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
143     {ADEID_NAME,        ADEDOFF_NAME_V2,     ADEDLEN_INIT},
144     {ADEID_COMMENT,     ADEDOFF_COMMENT_V2,  ADEDLEN_INIT},
145     {ADEID_FILEDATESI,  ADEDOFF_FILEDATESI,  ADEDLEN_FILEDATESI},
146     {ADEID_FINDERI,     ADEDOFF_FINDERI_V2,  ADEDLEN_FINDERI},
147     {ADEID_DID,         ADEDOFF_DID,         ADEDLEN_DID},
148     {ADEID_AFPFILEI,    ADEDOFF_AFPFILEI,    ADEDLEN_AFPFILEI},
149     {ADEID_SHORTNAME,   ADEDOFF_SHORTNAME,   ADEDLEN_INIT},
150     {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
151     {ADEID_PRIVDEV,     ADEDOFF_PRIVDEV,     ADEDLEN_INIT},
152     {ADEID_PRIVINO,     ADEDOFF_PRIVINO,     ADEDLEN_INIT},
153     {ADEID_PRIVSYN,     ADEDOFF_PRIVSYN,     ADEDLEN_INIT},
154     {ADEID_PRIVID,      ADEDOFF_PRIVID,      ADEDLEN_INIT},
155     {ADEID_RFORK,       ADEDOFF_RFORK_V2,    ADEDLEN_INIT},
156     {0, 0, 0}
157 };
158
159 /* Using Extended Attributes */
160 static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
161     {ADEID_FINDERI,    ADEDOFF_FINDERI_EA,    ADEDLEN_FINDERI},
162     {ADEID_COMMENT,    ADEDOFF_COMMENT_EA,    ADEDLEN_INIT},
163     {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI},
164     {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_EA,   ADEDLEN_AFPFILEI},
165     {ADEID_PRIVID,     ADEDOFF_PRIVID,        ADEDLEN_INIT},
166     {0, 0, 0}
167 };
168
169 /* fallback for EAs */
170 static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = {
171     {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
172     {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
173     {0, 0, 0}
174 };
175
176 #define ADFLAGS2LOGSTRBUFSIZ 128
177 const char *adflags2logstr(int adflags)
178 {
179     int first = 1;
180     static char buf[ADFLAGS2LOGSTRBUFSIZ];
181
182     buf[0] = 0;
183
184     if (adflags & ADFLAGS_DF) {
185         strlcat(buf, "DF", ADFLAGS2LOGSTRBUFSIZ);
186         first = 0;
187     }
188     if (adflags & ADFLAGS_RF) {
189         if (!first)
190             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
191         strlcat(buf, "RF", ADFLAGS2LOGSTRBUFSIZ);
192         first = 0;
193     }
194     if (adflags & ADFLAGS_NORF) {
195         if (!first)
196             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
197         strlcat(buf, "NORF", ADFLAGS2LOGSTRBUFSIZ);
198         first = 0;
199     }
200     if (adflags & ADFLAGS_HF) {
201         if (!first)
202             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
203         strlcat(buf, "HF", ADFLAGS2LOGSTRBUFSIZ);
204         first = 0;
205     }
206     if (adflags & ADFLAGS_NOHF) {
207         if (!first)
208             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
209         strlcat(buf, "NOHF", ADFLAGS2LOGSTRBUFSIZ);
210         first = 0;
211     }
212     if (adflags & ADFLAGS_DIR) {
213         if (!first)
214             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
215         strlcat(buf, "DIR", ADFLAGS2LOGSTRBUFSIZ);
216         first = 0;
217     }
218     if (adflags & ADFLAGS_CHECK_OF) {
219         if (!first)
220             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
221         strlcat(buf, "OF", ADFLAGS2LOGSTRBUFSIZ);
222         first = 0;
223     }
224     if (adflags & ADFLAGS_SETSHRMD) {
225         if (!first)
226             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
227         strlcat(buf, "SHRMD", ADFLAGS2LOGSTRBUFSIZ);
228         first = 0;
229     }
230     if (adflags & ADFLAGS_RDWR) {
231         if (!first)
232             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
233         strlcat(buf, "O_RDWR", ADFLAGS2LOGSTRBUFSIZ);
234         first = 0;
235     }
236     if (adflags & ADFLAGS_RDONLY) {
237         if (!first)
238             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
239         strlcat(buf, "O_RDONLY", ADFLAGS2LOGSTRBUFSIZ);
240         first = 0;
241     }
242     if (adflags & ADFLAGS_CREATE) {
243         if (!first)
244             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
245         strlcat(buf, "O_CREAT", ADFLAGS2LOGSTRBUFSIZ);
246         first = 0;
247     }
248     if (adflags & ADFLAGS_EXCL) {
249         if (!first)
250             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
251         strlcat(buf, "O_EXCL", ADFLAGS2LOGSTRBUFSIZ);
252         first = 0;
253     }
254     if (adflags & ADFLAGS_TRUNC) {
255         if (!first)
256             strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
257         strlcat(buf, "O_TRUNC", ADFLAGS2LOGSTRBUFSIZ);
258         first = 0;
259     }
260
261     return buf;
262 }
263
264 static uint32_t get_eid(uint32_t eid)
265 {
266     if (eid <= 15)
267         return eid;
268     if (eid == AD_DEV)
269         return ADEID_PRIVDEV;
270     if (eid == AD_INO)
271         return ADEID_PRIVINO;
272     if (eid == AD_SYN)
273         return ADEID_PRIVSYN;
274     if (eid == AD_ID)
275         return ADEID_PRIVID;
276
277     return 0;
278 }
279
280 /* ----------------------------------- */
281 static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp, int adflags)
282 {
283     const struct entry  *eid;
284     uint16_t            ashort;
285     struct stat         st;
286
287     LOG(log_debug, logtype_default, "new_ad_header(\"%s\")", path);
288
289     if (stp == NULL) {
290         stp = &st;
291         if (lstat(path, &st) != 0)
292             return -1;
293     }
294
295     ad->ad_magic = AD_MAGIC;
296     ad->ad_version = ad->ad_vers & 0x0f0000;
297     if (!ad->ad_version) {
298         ad->ad_version = AD_VERSION;
299     }
300
301     memset(ad->ad_data, 0, sizeof(ad->ad_data));
302
303     if (ad->ad_vers == AD_VERSION2)
304         eid = entry_order2;
305     else if (ad->ad_vers == AD_VERSION_EA)
306         eid = entry_order_ea;
307     else if (ad->ad_vers == AD_VERSION2_OSX)
308         eid = entry_order_osx;
309     else {
310         return -1;
311     }
312
313     while (eid->id) {
314         ad->ad_eid[eid->id].ade_off = eid->offset;
315         ad->ad_eid[eid->id].ade_len = eid->len;
316         eid++;
317     }
318
319     /* put something sane in the directory finderinfo */
320     if (ad->ad_vers != AD_VERSION2_OSX) {
321         if ((adflags & ADFLAGS_DIR)) {
322             /* set default view */
323             ashort = htons(FINDERINFO_CLOSEDVIEW);
324             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
325         } else {
326             /* set default creator/type fields */
327             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
328             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
329         }
330
331         /* make things invisible */
332         if ((ad->ad_options & ADVOL_INVDOTS)
333             && (*path == '.')
334             && !((adflags & ADFLAGS_DIR) && (path[1] == 0))
335             ) {
336             ashort = htons(ATTRBIT_INVISIBLE);
337             ad_setattr(ad, ashort);
338             ashort = htons(FINDERINFO_INVISIBLE);
339             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
340         }
341
342         /* put something sane in the date fields */
343         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
344         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
345         ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
346         ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
347     }
348     return 0;
349 }
350
351 /* -------------------------------------
352    read in the entries
353 */
354 static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
355 {
356     uint32_t   eid, len, off;
357     int        warning = 0;
358
359     /* now, read in the entry bits */
360     for (; nentries > 0; nentries-- ) {
361         memcpy(&eid, buf, sizeof( eid ));
362         eid = get_eid(ntohl(eid));
363         buf += sizeof( eid );
364         memcpy(&off, buf, sizeof( off ));
365         off = ntohl( off );
366         buf += sizeof( off );
367         memcpy(&len, buf, sizeof( len ));
368         len = ntohl( len );
369         buf += sizeof( len );
370
371         if (eid
372             && eid < ADEID_MAX
373             && off < sizeof(ad->ad_data)
374             && (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
375             ad->ad_eid[ eid ].ade_off = off;
376             ad->ad_eid[ eid ].ade_len = len;
377         } else if (!warning) {
378             warning = 1;
379             LOG(log_warning, logtype_default, "parse_entries: bogus eid: %d", eid);
380         }
381     }
382 }
383
384 /* this reads enough of the header so that we can figure out all of
385  * the entry lengths and offsets. once that's done, we just read/mmap
386  * the rest of the header in.
387  *
388  * NOTE: we're assuming that the resource fork is kept at the end of
389  *       the file. also, mmapping won't work for the hfs fs until it
390  *       understands how to mmap header files. */
391 static int ad_header_read(const char *path _U_, struct adouble *ad, struct stat *hst)
392 {
393     char                *buf = ad->ad_data;
394     uint16_t            nentries;
395     int                 len;
396     ssize_t             header_len;
397     struct stat         st;
398
399     /* read the header */
400     if ((header_len = adf_pread( ad->ad_mdp, buf, AD_DATASZ, 0)) < 0) {
401         return -1;
402     }
403     if (header_len < AD_HEADER_LEN) {
404         errno = EIO;
405         return -1;
406     }
407
408     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
409     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
410     ad->ad_magic = ntohl( ad->ad_magic );
411     ad->ad_version = ntohl( ad->ad_version );
412
413     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
414         LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
415         errno = EIO;
416         return -1;
417     }
418
419     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
420     nentries = ntohs( nentries );
421
422     /* read in all the entry headers. if we have more than the
423      * maximum, just hope that the rfork is specified early on. */
424     len = nentries*AD_ENTRY_LEN;
425
426     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
427         len = sizeof(ad->ad_data) - AD_HEADER_LEN;
428
429     buf += AD_HEADER_LEN;
430     if (len > header_len - AD_HEADER_LEN) {
431         LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
432         errno = EIO;
433         return -1;
434     }
435
436     /* figure out all of the entry offsets and lengths. if we aren't
437      * able to read a resource fork entry, bail. */
438     nentries = len / AD_ENTRY_LEN;
439     parse_entries(ad, buf, nentries);
440     if (!ad_getentryoff(ad, ADEID_RFORK)
441         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
442         ) {
443         LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset.");
444         errno = EIO;
445         return -1;
446     }
447
448     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
449         LOG(log_error, logtype_default, "ad_header_read: can't read in entries.");
450         errno = EIO;
451         return -1;
452     }
453
454     if (hst == NULL) {
455         hst = &st;
456         if (fstat(ad->ad_mdp->adf_fd, &st) < 0) {
457             return 1; /* fail silently */
458         }
459     }
460
461     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
462
463     return 0;
464 }
465
466 /* Read an ._ file, only uses the resofork, finderinfo is taken from EA */
467 static int ad_header_read_osx(const char *path _U_, struct adouble *ad, struct stat *hst)
468 {
469     EC_INIT;
470     struct adouble      adosx;
471     char                *buf = &adosx.ad_data[0];
472     uint16_t            nentries;
473     int                 len;
474     ssize_t             header_len;
475     struct stat         st;
476
477     memset(buf, 0, sizeof(adosx.ad_data));
478
479     /* read the header */
480     EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
481
482     if (header_len < AD_HEADER_LEN) {
483         errno = EIO;
484         return -1;
485     }
486
487     memcpy(&adosx.ad_magic, buf, sizeof(adosx.ad_magic));
488     memcpy(&adosx.ad_version, buf + ADEDOFF_VERSION, sizeof(adosx.ad_version));
489     adosx.ad_magic = ntohl(adosx.ad_magic);
490     adosx.ad_version = ntohl(adosx.ad_version);
491
492     if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
493         LOG(log_error, logtype_afpd, "ad_header_read_osx: can't parse AppleDouble header");
494         errno = EIO;
495         return -1;
496     }
497
498     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
499     nentries = ntohs(nentries);
500     len = nentries * AD_ENTRY_LEN;
501
502     if (len + AD_HEADER_LEN > sizeof(adosx.ad_data))
503         len = sizeof(adosx.ad_data) - AD_HEADER_LEN;
504
505     buf += AD_HEADER_LEN;
506     if (len > header_len - AD_HEADER_LEN) {
507         LOG(log_error, logtype_afpd, "ad_header_read_osx: can't read entry info.");
508         errno = EIO;
509         return -1;
510     }
511
512     nentries = len / AD_ENTRY_LEN;
513     parse_entries(&adosx, buf, nentries);
514
515     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
516         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
517         || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
518         ) {
519         LOG(log_error, logtype_afpd, "ad_header_read_osx: problem with rfork entry offset.");
520         errno = EIO;
521         return -1;
522     }
523
524     if (hst == NULL) {
525         hst = &st;
526         EC_NEG1( fstat(ad_reso_fileno(ad), &st) );
527     }
528
529     ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(&adosx, ADEID_RFORK));
530     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
531
532 EC_CLEANUP:
533     EC_EXIT;
534 }
535
536 static int ad_header_read_ea(const char *path, struct adouble *ad, struct stat *hst _U_)
537 {
538     uint16_t nentries;
539     int      len;
540     ssize_t  header_len;
541     char     *buf = ad->ad_data;
542
543     if (ad_meta_fileno(ad) != -1)
544         header_len = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA);
545     else
546         header_len = sys_lgetxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
547      if (header_len < 1) {
548         LOG(log_debug, logtype_default, "ad_header_read_ea: %s", strerror(errno));
549         return -1;
550     }
551
552     if (header_len < AD_HEADER_LEN) {
553         LOG(log_error, logtype_default, "ad_header_read_ea: bogus AppleDouble header.");
554         errno = EIO;
555         return -1;
556     }
557
558     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
559     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
560
561     ad->ad_magic = ntohl( ad->ad_magic );
562     ad->ad_version = ntohl( ad->ad_version );
563
564     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
565         LOG(log_error, logtype_default, "ad_header_read_ea: wrong magic or version");
566         errno = EIO;
567         return -1;
568     }
569
570     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
571     nentries = ntohs( nentries );
572
573     /* Protect against bogus nentries */
574     len = nentries * AD_ENTRY_LEN;
575     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
576         len = sizeof(ad->ad_data) - AD_HEADER_LEN;
577     if (len > header_len - AD_HEADER_LEN) {
578         LOG(log_error, logtype_default, "ad_header_read_ea: can't read entry info.");
579         errno = EIO;
580         return -1;
581     }
582     nentries = len / AD_ENTRY_LEN;
583
584     /* Now parse entries */
585     parse_entries(ad, buf + AD_HEADER_LEN, nentries);
586
587     return 0;
588 }
589
590 /*!
591  * Takes a path to an AppleDouble file and creates the parrent .AppleDouble directory
592  *
593  * Example:
594  * path: "/path/.AppleDouble/file"
595  * => mkdir("/path/.AppleDouble/") (in ad_mkdir())
596  */
597 static int ad_mkrf(const char *path)
598 {
599     char *slash;
600     /*
601      * Probably .AppleDouble doesn't exist, try to mkdir it.
602      */
603     if (NULL == ( slash = strrchr( path, '/' )) ) {
604         return -1;
605     }
606     *slash = '\0';
607     errno = 0;
608     if ( ad_mkdir( path, 0777 ) < 0 ) {
609         return -1;
610     }
611     *slash = '/';
612     return 0;
613 }
614
615 static int ad_mkrf_ea(const char *path _U_)
616 {
617     AFP_PANIC("ad_mkrf_ea: dont use");
618     return 0;
619 }
620
621 static int ad_mkrf_osx(const char *path _U_)
622 {
623     return 0;
624 }
625
626 /* ----------------
627    if we are root change path user/ group
628    It can be a native function for BSD cf. FAQ.Q10
629    path:  pathname to chown
630    stbuf: parent directory inode
631
632    use fstat and fchown or lchown with linux?
633 */
634 #define EMULATE_SUIDDIR
635
636 static int ad_chown(const char *path, struct stat *stbuf)
637 {
638     int ret = 0;
639 #ifdef EMULATE_SUIDDIR
640     uid_t id;
641
642     if (default_uid != (uid_t)-1) {
643         /* we are root (admin) */
644         id = (default_uid)?default_uid:stbuf->st_uid;
645         ret = lchown( path, id, stbuf->st_gid );
646     }
647 #endif
648     return ret;
649 }
650
651 #define DEFMASK 07700   /* be conservative */
652
653 /* ----------------
654    return access right and inode of path parent directory
655 */
656 static int ad_mode_st(const char *path, mode_t *mode, struct stat *stbuf)
657 {
658     if (*mode == 0) {
659         return -1;
660     }
661     if (ad_stat(path, stbuf) != 0) {
662         *mode &= DEFMASK;
663         return -1;
664     }
665     *mode &= stbuf->st_mode;
666     return 0;
667 }
668
669 /* --------------------------- */
670 static int ad_header_upgrade(struct adouble *ad _U_, const char *name _U_)
671 {
672     return 0;
673 }
674
675 static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
676 {
677     AFP_PANIC("ad_header_upgrade_ea: dont use");
678     return 0;
679 }
680
681 /*!
682  * Error handling for adouble header(=metadata) file open error
683  *
684  * We're called because opening ADFLAGS_HF caused an error.
685  * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
686  * 2. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
687  *    ought to close it before returning with an error condition.
688  */
689 static int ad_error(struct adouble *ad, int adflags)
690 {
691     int err = errno;
692     if (adflags & ADFLAGS_NOHF) { /* 1 */
693         return 0;
694     }
695     if (adflags & ADFLAGS_DF) { /* 2 */
696         ad_close( ad, ADFLAGS_DF );
697         err = errno;
698     }
699     return -1 ;
700 }
701
702 /* Map ADFLAGS to open() flags */
703 static int ad2openflags(int adflags)
704 {
705     int oflags = 0;
706
707     if (adflags & ADFLAGS_RDWR)
708         oflags |= O_RDWR;
709     if (adflags & ADFLAGS_RDONLY) {
710         if (adflags & ADFLAGS_SETSHRMD)
711             oflags |= O_RDWR;
712         else
713             oflags |= O_RDONLY;
714     }
715     if (adflags & ADFLAGS_CREATE)
716         oflags |= O_CREAT;
717     if (adflags & ADFLAGS_EXCL)
718         oflags |= O_EXCL;
719     if (adflags & ADFLAGS_TRUNC)
720         oflags |= O_TRUNC;
721
722     return oflags;
723 }
724
725 static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble *ad)
726 {
727     struct stat st_dir;
728     int         oflags;
729     mode_t      admode;
730     int         st_invalid = -1;
731     ssize_t     lsz;
732
733     LOG(log_debug, logtype_default, "ad_open_df(\"%s\", %04o)",
734         fullpathname(path), mode);
735
736     if (ad_data_fileno(ad) != -1) {
737         /* the file is already open, but we want write access: */
738         if ((adflags & ADFLAGS_RDWR)
739             /* and it was denied the first time: */
740             && (ad->ad_data_fork.adf_flags & O_RDONLY)) {
741                 errno = EACCES;
742                 return -1;
743             }
744         /* it's not new anymore */
745         ad->ad_data_fork.adf_flags &= ~( O_TRUNC | O_CREAT );
746         ad->ad_data_fork.adf_refcount++;
747         return 0;
748     }
749
750     oflags = O_NOFOLLOW | ad2openflags(adflags);
751
752     admode = mode;
753     if ((adflags & ADFLAGS_CREATE)) {
754         st_invalid = ad_mode_st(path, &admode, &st_dir);
755         if ((ad->ad_options & ADVOL_UNIXPRIV))
756             admode = mode;
757     }
758
759     ad->ad_data_fork.adf_fd = open(path, oflags, admode);
760
761     if (ad->ad_data_fork.adf_fd == -1) {
762         switch (errno) {
763         case EACCES:
764         case EPERM:
765         case EROFS:
766             if ((adflags & ADFLAGS_SETSHRMD) && (adflags & ADFLAGS_RDONLY)) {
767                 oflags &= ~O_RDWR;
768                 oflags |= O_RDONLY;
769                 if ((ad->ad_data_fork.adf_fd = open(path, oflags, admode)) == -1)
770                     return -1;
771                 break;
772             }
773             return -1;
774         case OPEN_NOFOLLOW_ERRNO:
775             ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
776             if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
777                 free(ad->ad_data_fork.adf_syml);
778                 return -1;
779             }
780             ad->ad_data_fork.adf_syml[lsz] = 0;
781             ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
782             break;
783         default:
784             return -1;
785         }
786     }
787
788     if (!st_invalid)
789         ad_chown(path, &st_dir); /* just created, set owner if admin (root) */
790
791     ad->ad_data_fork.adf_flags = oflags;
792     adf_lock_init(&ad->ad_data_fork);
793     ad->ad_data_fork.adf_refcount++;
794
795     return 0;
796 }
797
798 /* TODO: error handling */
799 static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adouble *ad)
800 {
801     struct stat st_dir;
802     struct stat st_meta;
803     struct stat *pst = NULL;
804     const char  *ad_p;
805     int         oflags, nocreatflags;
806     mode_t      admode;
807     int         st_invalid = -1;
808
809     if (ad_meta_fileno(ad) != -1) {
810         /* the file is already open, but we want write access: */
811         if ((adflags & ADFLAGS_RDWR) &&
812             /* and it was already denied: */
813             (ad->ad_mdp->adf_flags & O_RDONLY)) {
814             errno = EACCES;
815             return -1;
816         }
817         ad_refresh(path, ad);
818         /* it's not new anymore */
819         ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
820         ad->ad_mdp->adf_refcount++;
821         return 0;
822     }
823
824     ad_p = ad->ad_ops->ad_path(path, adflags);
825     oflags = O_NOFOLLOW | ad2openflags(adflags);
826     nocreatflags = oflags & ~(O_CREAT | O_EXCL);
827
828     ad->ad_mdp->adf_fd = open(ad_p, nocreatflags);
829
830     if (ad->ad_mdp->adf_fd != -1) {
831         ad->ad_mdp->adf_flags = nocreatflags;
832     } else {
833         switch (errno) {
834         case EACCES:
835         case EPERM:
836         case EROFS:
837             if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) {
838                 nocreatflags &= ~O_RDWR;
839                 nocreatflags |= O_RDONLY;
840                 if ((ad->ad_mdp->adf_fd = open(ad_p, nocreatflags)) == -1)
841                     return -1;
842                 ad->ad_mdp->adf_flags = nocreatflags;
843                 break;
844             }
845             return -1;
846         case ENOENT:
847             if (!(oflags & O_CREAT))
848                 return ad_error(ad, adflags);
849             /*
850              * We're expecting to create a new adouble header file here
851              */
852             LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
853                 fullpathname(path));
854             admode = mode;
855             errno = 0;
856             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
857             if ((ad->ad_options & ADVOL_UNIXPRIV))
858                 admode = mode;
859             admode = ad_hf_mode(admode);
860             if (errno == ENOENT) {
861                 if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
862                     return ad_error(ad, adflags);
863                 }
864                 admode = mode;
865                 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
866                 if ((ad->ad_options & ADVOL_UNIXPRIV))
867                     admode = mode;
868                 admode = ad_hf_mode(admode);
869             }
870
871             /* retry with O_CREAT */
872             ad->ad_mdp->adf_fd = open(ad_p, oflags, admode);
873             if ( ad->ad_mdp->adf_fd < 0 )
874                 return ad_error(ad, adflags);
875
876             ad->ad_mdp->adf_flags = oflags;
877             /* just created, set owner if admin owner (root) */
878             if (!st_invalid)
879                 ad_chown(ad_p, &st_dir);
880             break;
881         default:
882             return -1;
883         }
884     }
885
886     if (!(ad->ad_mdp->adf_flags & O_CREAT)) {
887         /* check for 0 length files, treat them as new. */
888         if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0) {
889             if (st_meta.st_size == 0)
890                 ad->ad_mdp->adf_flags |= O_TRUNC;
891             else
892                 /* we have valid data in st_meta stat structure, reused it in ad_header_read */
893                 pst = &st_meta;
894         }
895     }
896
897     adf_lock_init(ad->ad_mdp);
898     ad->ad_mdp->adf_refcount = 1;
899
900     if ((ad->ad_mdp->adf_flags & ( O_TRUNC | O_CREAT ))) {
901         /* This is a new adouble header file, create it */
902         if (new_ad_header(ad, path, pst, adflags) < 0) {
903             int err = errno;
904             /* the file is already deleted, perm, whatever, so return an error */
905             ad_close(ad, adflags);
906             errno = err;
907             return -1;
908         }
909         ad_flush(ad);
910     } else {
911         /* Read the adouble header in and parse it.*/
912         if (ad->ad_ops->ad_header_read(path, ad, pst) < 0
913             || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) {
914             int err = errno;
915             ad_close(ad, adflags);
916             errno = err;
917             return -1;
918         }
919     }
920
921     return 0;
922 }
923
924 static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
925 {
926     EC_INIT;
927     ssize_t rforklen;
928     int oflags;
929     int opened = 0;
930
931     LOG(log_debug, logtype_default,
932         "ad_open_hf_ea(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
933         fullpathname(path), adflags2logstr(adflags),
934         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
935         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
936         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
937
938     oflags = O_NOFOLLOW | (ad2openflags(adflags) & ~(O_CREAT | O_TRUNC));
939
940     if (ad_meta_fileno(ad) != -1) {
941         /* the file is already open, but we want write access: */
942         if ((oflags & O_RDWR) &&
943             /* and it was already denied: */
944             (ad->ad_mdp->adf_flags & O_RDONLY)) {
945             LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s",
946                 fullpathname(path), strerror(errno));
947             errno = EACCES;
948             EC_FAIL;
949         }
950         /* it's not new anymore */
951         ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
952     } else {
953         if (oflags & O_RDWR) {
954             /* Fo a RDONLY adouble we just use sys_lgetxattr instead if sys_fgetxattr */
955             if (adflags & ADFLAGS_DIR)
956                 /* For directories we open the directory RDONYL so we can later fchdir()  */
957                 oflags = (oflags & ~O_RDWR) | O_RDONLY;
958             LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
959             EC_NEG1(ad_meta_fileno(ad) = open(path, oflags));
960             opened = 1;
961             ad->ad_mdp->adf_flags = oflags;
962         }
963     }
964
965     /* Read the adouble header in and parse it.*/
966     if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) {
967         LOG(log_error, logtype_default, "ad_open_hf_ea: no EA adouble");
968
969         if (!(adflags & ADFLAGS_CREATE)) {
970             errno = ENOENT;
971             EC_FAIL;
972         }
973
974         LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
975
976         /* It doesnt exist, EPERM or another error */
977         if (!(errno == ENOATTR || errno == ENOENT)) {
978             LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
979             EC_FAIL;
980         }
981
982         /* Create one */
983         EC_NEG1_LOG(new_ad_header(ad, path, NULL, adflags));
984         ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
985         ad_flush(ad);
986         LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
987     }
988
989     ad->ad_mdp->adf_refcount++;
990     (void)ad_reso_size(path, adflags, ad);
991
992 EC_CLEANUP:
993     if (ret != 0 && opened && ad_meta_fileno(ad) != -1) {
994         close(ad_meta_fileno(ad));
995         ad_meta_fileno(ad) = -1;
996         ad->ad_mdp->adf_refcount = 0;
997     }
998     LOG(log_debug, logtype_default,
999         "ad_open_hf_ea(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1000         fullpathname(path), adflags2logstr(adflags), ret,
1001         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1002         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1003         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1004         
1005     EC_EXIT;
1006 }
1007
1008 static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *ad)
1009 {
1010     int ret = 0;
1011
1012     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1013     ad->ad_rlen = 0;
1014
1015     switch (ad->ad_vers) {
1016     case AD_VERSION2:
1017         ret = ad_open_hf_v2(path, adflags, mode, ad);
1018         break;
1019     case AD_VERSION_EA:
1020         ret = ad_open_hf_ea(path, adflags, mode, ad);
1021         break;
1022     default:
1023         ret = -1;
1024         break;
1025     }
1026
1027     if (ret != 0)
1028         ret = ad_error(ad, adflags);
1029
1030     return ret;
1031 }
1032
1033 /*!
1034  * Get resofork length for adouble:ea
1035  */
1036 static int ad_reso_size(const char *path, int adflags, struct adouble *ad)
1037 {
1038     EC_INIT;
1039     struct stat st;
1040
1041     LOG(log_debug, logtype_default, "ad_reso_size(\"%s\")", path);
1042
1043 #ifdef HAVE_EAFD
1044     int opened = 0;
1045     int eafd = ad_reso_fileno(ad);
1046     if (eafd == -1) {
1047         EC_NEG1( eafd = sys_getxattrfd(path, O_RDONLY) );
1048         opened = 1;
1049     }
1050     EC_NEG1( rlen = fstat(eafd, &st) );
1051     ad->ad_rlen = st.st_size;
1052 #else
1053     const char *rfpath;
1054     EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags));
1055     EC_ZERO( lstat(rfpath, &st));
1056     if (st.st_size > ADEDOFF_RFORK_OSX)
1057         ad->ad_rlen = st.st_size - ADEDOFF_RFORK_OSX;
1058     else
1059         ad->ad_rlen = 0;
1060 #endif
1061
1062     LOG(log_debug, logtype_default, "ad_reso_size(\"%s\"): size: %zd", path, ad->ad_rlen);
1063
1064 EC_CLEANUP:
1065 #ifdef HAVE_EAFD
1066     if (opened)
1067         close(eafd);
1068 #endif
1069     if (ret != 0)
1070         ad->ad_rlen = 0;
1071     EC_EXIT;
1072 }
1073
1074 /*!
1075  * Open ressource fork
1076  *
1077  * Only for adouble:ea, a nullop otherwise because adouble:v2 has the ressource fork as part
1078  * of the adouble file which is openend by ADFLAGS_HF.
1079  */
1080 static int ad_open_rf(const char *path, int adflags, int mode, struct adouble *ad)
1081 {
1082     EC_INIT;
1083     int oflags;
1084     int opened = 0;
1085     int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF);
1086     ssize_t rlen;
1087 #ifndef HAVE_EAFD
1088     const char *rfpath;
1089     struct stat st;
1090 #endif
1091
1092     if (ad->ad_vers != AD_VERSION_EA)
1093         return 0;
1094
1095     LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
1096
1097     oflags = O_NOFOLLOW | (ad2openflags(adflags) & ~O_CREAT);
1098
1099     if (ad_reso_fileno(ad) != -1) {
1100         /* the file is already open, but we want write access: */
1101         if ((oflags & O_RDWR)
1102             /* and it was already denied: */
1103             && (ad->ad_rfp->adf_flags & O_RDONLY)) {
1104             errno = EACCES;
1105             EC_FAIL;
1106         }
1107         ad->ad_rfp->adf_flags &= ~( O_TRUNC | O_CREAT );
1108         ad->ad_rfp->adf_refcount++;
1109         EC_NEG1_LOG( ad_reso_size(path, adflags, ad));
1110         goto EC_CLEANUP;
1111     }
1112 #ifdef HAVE_EAFD
1113     if ((ad_reso_fileno(ad) = sys_getxattrfd(path, oflags)) == -1) {
1114         if (!(adflags & ADFLAGS_CREATE))
1115             EC_FAIL;
1116         oflags |= O_CREAT;
1117         EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(path, oflags, 0666) ); 
1118     }
1119 #else
1120     EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags) );
1121     if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
1122         if (!(adflags & ADFLAGS_CREATE))
1123             EC_FAIL;
1124         oflags |= O_CREAT;
1125         EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
1126         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
1127             path, rfpath);
1128     }
1129 #endif
1130     opened = 1;
1131     ad->ad_rfp->adf_refcount = 1;
1132     ad->ad_rfp->adf_flags = oflags;
1133
1134 #ifndef HAVE_EAFD
1135     EC_ZERO_LOG( fstat(ad_reso_fileno(ad), &st) );
1136     if (ad->ad_rfp->adf_flags & O_CREAT) {
1137         /* This is a new adouble header file, create it */
1138         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
1139             path, rfpath);
1140         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
1141             path, rfpath);
1142         ad_flush(ad);
1143     } else {
1144         /* Read the adouble header */
1145         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
1146             path, rfpath);
1147         EC_NEG1_LOG( ad_header_read_osx(NULL, ad, &st) );
1148     }
1149 #endif
1150
1151     (void)ad_reso_size(path, adflags, ad);
1152
1153 EC_CLEANUP:
1154     if (ret != 0) {
1155         if (opened && (ad_reso_fileno(ad) != -1)) {
1156             close(ad_reso_fileno(ad));
1157             ad_reso_fileno(ad) = -1;
1158             ad->ad_rfp->adf_refcount = 0;
1159         }
1160         if (adflags & ADFLAGS_NORF) {
1161             ret = 0;
1162         } else {
1163             int err = errno;
1164             (void)ad_close(ad, closeflags);
1165             errno = err;
1166         }
1167         ad->ad_rlen = 0;
1168     }
1169
1170     LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
1171
1172     EC_EXIT;
1173 }
1174
1175 /***********************************************************************************
1176  * API functions
1177  ********************************************************************************* */
1178
1179 const char *ad_path_ea( const char *path, int adflags _U_)
1180 {
1181     return path;
1182 }
1183
1184 const char *ad_path_osx(const char *path, int adflags _U_)
1185 {
1186     static char pathbuf[ MAXPATHLEN + 1];
1187     char    c, *slash, buf[MAXPATHLEN + 1];
1188
1189     if (!strcmp(path,".")) {
1190         /* fixme */
1191         getcwd(buf, MAXPATHLEN);
1192     }
1193     else {
1194         strlcpy(buf, path, MAXPATHLEN +1);
1195     }
1196     if (NULL != ( slash = strrchr( buf, '/' )) ) {
1197         c = *++slash;
1198         *slash = '\0';
1199         strlcpy( pathbuf, buf, MAXPATHLEN +1);
1200         *slash = c;
1201     } else {
1202         pathbuf[ 0 ] = '\0';
1203         slash = buf;
1204     }
1205     strlcat( pathbuf, "._", MAXPATHLEN  +1);
1206     strlcat( pathbuf, slash, MAXPATHLEN +1);
1207     return pathbuf;
1208 }
1209
1210 /*
1211  * Put the .AppleDouble where it needs to be:
1212  *
1213  *      /   a/.AppleDouble/b
1214  *  a/b
1215  *      \   b/.AppleDouble/.Parent
1216  *
1217  * FIXME: should do something for pathname > MAXPATHLEN
1218  */
1219 const char *ad_path( const char *path, int adflags)
1220 {
1221     static char pathbuf[ MAXPATHLEN + 1];
1222     const char *slash;
1223     size_t  l ;
1224
1225     if ( adflags & ADFLAGS_DIR ) {
1226         l = strlcpy( pathbuf, path, sizeof(pathbuf));
1227
1228         if ( l && l < MAXPATHLEN) {
1229             pathbuf[l++] = '/';
1230         }
1231         strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
1232     } else {
1233         if (NULL != ( slash = strrchr( path, '/' )) ) {
1234             slash++;
1235             l = slash - path;
1236             /* XXX we must return NULL here and test in the caller */
1237             if (l > MAXPATHLEN)
1238                 l = MAXPATHLEN;
1239             memcpy( pathbuf, path, l);
1240         } else {
1241             l = 0;
1242             slash = path;
1243         }
1244         l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
1245         strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
1246     }
1247
1248     return( pathbuf );
1249 }
1250
1251 /* -------------------------
1252  * Support inherited protection modes for AppleDouble files.  The supplied
1253  * mode is ANDed with the parent directory's mask value in lieu of "umask",
1254  * and that value is returned.
1255  */
1256 char *ad_dir(const char *path)
1257 {
1258     static char     modebuf[ MAXPATHLEN + 1];
1259     char        *slash;
1260     /*
1261      * For a path with directories in it, remove the final component
1262      * (path or subdirectory name) to get the name we want to stat.
1263      * For a path which is just a filename, use "." instead.
1264      */
1265     slash = strrchr( path, '/' );
1266     if (slash) {
1267         size_t len;
1268
1269         len = slash - path;
1270         if (len >= MAXPATHLEN) {
1271             errno = ENAMETOOLONG;
1272             return NULL;  /* can't do it */
1273         }
1274         memcpy( modebuf, path, len );
1275         modebuf[len] = '\0';
1276         /* is last char a '/' ? */
1277         if (slash[1] == 0) {
1278             slash = modebuf+ len;
1279             /* remove them */
1280             while (modebuf < slash && slash[-1] == '/') {
1281                 --slash;
1282             }
1283             if (modebuf == slash) {
1284                 goto use_cur;
1285             }
1286             *slash = '\0';
1287             while (modebuf < slash && *slash != '/') {
1288                 --slash;
1289             }
1290             if (modebuf == slash) {
1291                 goto use_cur;
1292             }
1293             *slash = '\0';      /* remove pathname component */
1294         }
1295         return modebuf;
1296     }
1297 use_cur:
1298     modebuf[0] = '.';   /* use current directory */
1299     modebuf[1] = '\0';
1300     return modebuf;
1301 }
1302
1303 int ad_setfuid(const uid_t id)
1304 {
1305     default_uid = id;
1306     return 0;
1307 }
1308
1309 /* ---------------- */
1310 uid_t ad_getfuid(void)
1311 {
1312     return default_uid;
1313 }
1314
1315 /* ----------------
1316    stat path parent directory
1317 */
1318 int ad_stat(const char *path, struct stat *stbuf)
1319 {
1320     char *p;
1321
1322     p = ad_dir(path);
1323     if (!p)
1324         return -1;
1325     return lstat( p, stbuf );
1326 }
1327
1328 /* ----------------
1329    return access right of path parent directory
1330 */
1331 int ad_mode( const char *path, mode_t mode)
1332 {
1333     struct stat     stbuf;
1334     ad_mode_st(path, &mode, &stbuf);
1335     return mode;
1336 }
1337
1338 /*
1339  * Use mkdir() with mode bits taken from ad_mode().
1340  */
1341 int ad_mkdir( const char *path, mode_t mode)
1342 {
1343     int ret;
1344     int st_invalid;
1345     struct stat stbuf;
1346
1347     LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
1348         path, mode, getcwdpath());
1349
1350     st_invalid = ad_mode_st(path, &mode, &stbuf);
1351     ret = mkdir( path, mode );
1352     if (ret || st_invalid)
1353         return ret;
1354     ad_chown(path, &stbuf);
1355
1356     return ret;
1357 }
1358
1359 static void ad_init_func(struct adouble *ad)
1360 {
1361     switch (ad->ad_vers) {
1362     case AD_VERSION2:
1363         ad->ad_ops = &ad_adouble;
1364         ad->ad_rfp = &ad->ad_resource_fork;
1365         ad->ad_mdp = &ad->ad_resource_fork;
1366         break;
1367     case AD_VERSION_EA:
1368         ad->ad_ops = &ad_adouble_ea;
1369         ad->ad_rfp = &ad->ad_resource_fork;
1370         ad->ad_mdp = &ad->ad_data_fork;
1371         break;
1372     default:
1373         AFP_PANIC("ad_init: unknown AD version");
1374     }
1375
1376
1377     ad_data_fileno(ad) = -1;
1378     ad_reso_fileno(ad) = -1;
1379     ad_meta_fileno(ad) = -1;
1380     ad->ad_refcount = 1;
1381     return;
1382 }
1383
1384 void ad_init_old(struct adouble *ad, int flags, int options)
1385 {
1386     memset(ad, 0, sizeof(struct adouble));
1387     ad->ad_vers = flags;
1388     ad->ad_options = options;
1389     ad_init_func(ad);
1390 }
1391
1392 void ad_init(struct adouble *ad, const struct vol * restrict vol)
1393 {
1394     memset(ad, 0, sizeof(struct adouble));
1395     ad->ad_vers = vol->v_adouble;
1396     ad->ad_options = vol->v_ad_options;
1397     ad_init_func(ad);
1398 }
1399
1400 /*!
1401  * Open data-, metadata(header)- or ressource fork
1402  *
1403  * ad_open(struct adouble *ad, const char *path, int adflags, int flags)
1404  * ad_open(struct adouble *ad, const char *path, int adflags, int flags, mode_t mode)
1405  *
1406  * You must call ad_init() before ad_open, usually you'll just call it like this: \n
1407  * @code
1408  *      struct adoube ad;
1409  *      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1410  * @endcode
1411  *
1412  * Open a files data fork, metadata fork or ressource fork.
1413  *
1414  * @param ad        (rw) pointer to struct adouble
1415  * @param path      (r)  Path to file or directory
1416  * @param adflags   (r)  Flags specifying which fork to open, can be or'd:
1417  *                         ADFLAGS_DF:        open data fork
1418  *                         ADFLAGS_RF:        open ressource fork
1419  *                         ADFLAGS_HF:        open header (metadata) file
1420  *                         ADFLAGS_NOHF:      it's not an error if header file couldn't be opened
1421  *                         ADFLAGS_NORF:      it's not an error if reso fork couldn't be opened
1422  *                         ADFLAGS_DIR:       if path is a directory you MUST or ADFLAGS_DIR to adflags
1423  *
1424  *                       Access mode for the forks:
1425  *                         ADFLAGS_RDONLY:    open read only
1426  *                         ADFLAGS_RDWR:      open read write
1427  *
1428  *                       Creation flags:
1429  *                         ADFLAGS_CREATE:    create if not existing
1430  *                         ADFLAGS_TRUNC:     truncate
1431  *
1432  *                       Special flags:
1433  *                         ADFLAGS_CHECK_OF:  check for open forks from us and other afpd's
1434  *                         ADFLAGS_SETSHRMD:  this adouble struct will be used to set sharemode locks.
1435  *                                            This basically results in the files being opened RW instead of RDONLY.
1436  * @param mode      (r)  mode used with O_CREATE
1437  *
1438  * The open mode flags (rw vs ro) have to take into account all the following requirements:
1439  * - we remember open fds for files because me must avoid a single close releasing fcntl locks for other
1440  *   fds of the same file
1441  *
1442  * @returns 0 on success, any other value indicates an error
1443  */
1444 int ad_open(struct adouble *ad, const char *path, int adflags, ...)
1445 {
1446     EC_INIT;
1447     va_list args;
1448     mode_t mode = 0;
1449
1450     LOG(log_debug, logtype_default,
1451         "ad_open(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1452         fullpathname(path), adflags2logstr(adflags),
1453         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1454         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1455         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1456
1457     if (adflags & ADFLAGS_CHECK_OF)
1458         /* Checking for open forks requires sharemode lock support (ie RDWR instead of RDONLY) */
1459         adflags |= ADFLAGS_SETSHRMD;
1460
1461     if ((ad->ad_vers == AD_VERSION2) && (adflags & ADFLAGS_RF)) {
1462         adflags |= ADFLAGS_HF;
1463         if (adflags & ADFLAGS_NORF)
1464             adflags |= ADFLAGS_NOHF;
1465     }
1466
1467     if (ad->ad_inited != AD_INITED) {
1468         ad->ad_adflags = adflags;
1469         ad->ad_inited = AD_INITED;
1470     } else {
1471         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1472         if (ad->ad_resource_fork.adf_refcount > 0)
1473             ad->ad_open_forks |= ATTRBIT_ROPEN;
1474     }
1475
1476     va_start(args, adflags);
1477     if (adflags & ADFLAGS_CREATE)
1478         mode = va_arg(args, mode_t);
1479     va_end(args);
1480
1481     if (adflags & ADFLAGS_DF) {
1482         EC_ZERO( ad_open_df(path, adflags, mode, ad) );
1483     }
1484
1485     if (adflags & ADFLAGS_HF) {
1486         EC_ZERO( ad_open_hf(path, adflags, mode, ad) );
1487     }
1488
1489     if (adflags & ADFLAGS_RF) {
1490         EC_ZERO( ad_open_rf(path, adflags, mode, ad) );
1491     }
1492
1493     if (adflags & ADFLAGS_CHECK_OF) {
1494         ad->ad_open_forks |= ad_openforks(ad, ad->ad_open_forks);
1495     }
1496
1497 EC_CLEANUP:
1498     LOG(log_debug, logtype_default,
1499         "ad_open(\"%s\"): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1500         fullpathname(path), ret,
1501         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1502         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1503         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1504
1505     EC_EXIT;
1506 }
1507
1508 /*!
1509  * @brief open metadata, possibly as root
1510  *
1511  * Return only metadata but try very hard ie at first try as user, then try as root.
1512  *
1513  * @param name  name of file/dir
1514  * @param flags ADFLAGS_DIR: name is a directory \n
1515  *              ADFLAGS_CHECK_OF: test if name is open by us or another afpd process
1516  *
1517  * @param adp   pointer to struct adouble
1518  */
1519 int ad_metadata(const char *name, int flags, struct adouble *adp)
1520 {
1521     uid_t uid;
1522     int   ret, err, oflags;
1523
1524     /* Sanitize flags */
1525     oflags = (flags & (ADFLAGS_CHECK_OF | ADFLAGS_DIR)) | ADFLAGS_HF | ADFLAGS_RDONLY;    
1526
1527     if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) {
1528         uid = geteuid();
1529         if (seteuid(0)) {
1530             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1531             errno = EACCES;
1532             return -1;
1533         }
1534         /* we are root open read only */
1535         ret = ad_open(adp, name, oflags);
1536         err = errno;
1537         if ( seteuid(uid) < 0) {
1538             LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1539             exit(EXITERR_SYS);
1540         }
1541         errno = err;
1542     }
1543
1544     return ret;
1545 }
1546
1547 /*
1548  * @brief openat like wrapper for ad_metadata
1549  */
1550 int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
1551 {
1552     int ret = 0;
1553     int cwdfd = -1;
1554
1555     if (dirfd != -1) {
1556         if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
1557             ret = -1;
1558             goto exit;
1559         }
1560     }
1561
1562     if (ad_metadata(name, flags, adp) < 0) {
1563         ret = -1;
1564         goto exit;
1565     }
1566
1567     if (dirfd != -1) {
1568
1569         if (fchdir(cwdfd) != 0) {
1570             LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
1571             exit(EXITERR_SYS);
1572         }
1573     }
1574
1575 exit:
1576     if (cwdfd != -1)
1577         close(cwdfd);
1578
1579     return ret;
1580
1581 }
1582
1583 int ad_refresh(const char *path, struct adouble *ad)
1584 {
1585     switch (ad->ad_vers) {
1586     case AD_VERSION2:
1587         if (ad_meta_fileno(ad) == -1)
1588             return -1;
1589         return ad->ad_ops->ad_header_read(NULL, ad, NULL);
1590         break;
1591     case AD_VERSION_EA:
1592 #ifdef HAVE_EAFD
1593         if (AD_META_OPEN(ad)) {
1594             if (ad_data_fileno(ad) == -1)
1595                 return -1;
1596             // TODO: read meta EA
1597         }
1598
1599         if (AD_RSRC_OPEN(ad)) {
1600             if (ad_reso_fileno(ad) == -1)
1601                 return -1;
1602             ssize_t len;
1603             if ((len = fstat(ad_reso_fileno(ad))) == -1)
1604                 return -1;
1605             ad->ad_rlen = len;
1606         }
1607 #else
1608         if (AD_META_OPEN(ad)) {
1609             if (ad_data_fileno(ad) == -1)
1610                 return -1;
1611             // TODO: read meta EA
1612         }
1613         if (AD_RSRC_OPEN(ad)) {
1614             if (ad_reso_fileno(ad) == -1)
1615                 return -1;
1616             if (ad_header_read_osx(path, ad, NULL) < 0)
1617                 return -1;
1618         }
1619 #endif
1620         return ad->ad_ops->ad_header_read(path, ad, NULL);
1621         break;
1622     default:
1623         return -1;
1624         break;
1625     }
1626
1627 }
1628
1629 int ad_openat(struct adouble  *ad,
1630               int dirfd,  /* dir fd openat like */
1631               const char *path,
1632               int adflags, ...)
1633 {
1634     EC_INIT;
1635     int cwdfd = -1;
1636     va_list args;
1637     mode_t mode;
1638
1639     if (dirfd != -1) {
1640         if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0))
1641             EC_FAIL;
1642     }
1643
1644     va_start(args, adflags);
1645     if (adflags & ADFLAGS_CREATE)
1646         mode = va_arg(args, mode_t);
1647     va_end(args);
1648
1649     EC_NEG1( ad_open(ad, path, adflags, mode) );
1650
1651     if (dirfd != -1) {
1652         if (fchdir(cwdfd) != 0) {
1653             AFP_PANIC("ad_openat: cant chdir back");
1654         }
1655     }
1656
1657 EC_CLEANUP:
1658     if (cwdfd != -1)
1659         close(cwdfd);
1660
1661     return ret;
1662 }