]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
Redesign ad_open API to only use one arg for all flags, fix locking for adouble:v2
[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
54 #include "ad_lock.h"
55
56 #ifndef MAX
57 #define MAX(a, b)  ((a) < (b) ? (b) : (a))
58 #endif /* ! MAX */
59
60 #define ADEDOFF_MAGIC        (0)
61 #define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
62 #define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
63 #define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
64
65 /* initial lengths of some of the fields */
66 #define ADEDLEN_INIT     0
67
68 /* i stick things in a slightly different order than their eid order in
69  * case i ever want to separate RootInfo behaviour from the rest of the
70  * stuff. */
71
72 /* ad:v2 */
73 #define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
74 #define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
75 #define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
76 #define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
77 #define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
78 #define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
79 #define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
80 #define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
81 #define ADEDOFF_PRIVDEV      (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
82 #define ADEDOFF_PRIVINO      (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
83 #define ADEDOFF_PRIVSYN      (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
84 #define ADEDOFF_PRIVID       (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
85 #define ADEDOFF_RFORK_V2     (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
86
87 /* ad:ea */
88 #define ADEDOFF_FINDERI_EA   (AD_HEADER_LEN + ADEID_NUM_EA * AD_ENTRY_LEN)
89 #define ADEDOFF_COMMENT_EA   (ADEDOFF_FINDERI_EA + ADEDLEN_FINDERI)
90 #define ADEDOFF_FILEDATESI_EA (ADEDOFF_COMMENT_EA + ADEDLEN_COMMENT)
91 #define ADEDOFF_AFPFILEI_EA  (ADEDOFF_FILEDATESI_EA + ADEDLEN_FILEDATESI)
92
93 /* this is to prevent changing timezones from causing problems with
94    localtime volumes. the screw-up is 30 years. we use a delta of 5 years */
95 #define TIMEWARP_DELTA 157680000
96
97 struct entry {
98     uint32_t id, offset, len;
99 };
100
101 /* --------------------------- */
102 static uid_t default_uid = -1;
103
104 /* Forward declarations */
105 static int ad_mkrf(const char *path);
106 static int ad_header_read(struct adouble *ad, struct stat *hst);
107 static int ad_header_upgrade(struct adouble *ad, const char *name);
108
109 static int ad_mkrf_ea(const char *path);
110 static int ad_header_read_ea(struct adouble *ad, struct stat *hst);
111 static int ad_header_upgrade_ea(struct adouble *ad, const char *name);
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     &ad_path_ea,
123     &ad_mkrf_ea,
124     &ad_rebuild_adouble_header,
125     &ad_header_read_ea,
126     &ad_header_upgrade_ea,
127 };
128
129 static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
130     {ADEID_NAME,        ADEDOFF_NAME_V2,     ADEDLEN_INIT},
131     {ADEID_COMMENT,     ADEDOFF_COMMENT_V2,  ADEDLEN_INIT},
132     {ADEID_FILEDATESI,  ADEDOFF_FILEDATESI,  ADEDLEN_FILEDATESI},
133     {ADEID_FINDERI,     ADEDOFF_FINDERI_V2,  ADEDLEN_FINDERI},
134     {ADEID_DID,         ADEDOFF_DID,         ADEDLEN_DID},
135     {ADEID_AFPFILEI,    ADEDOFF_AFPFILEI,    ADEDLEN_AFPFILEI},
136     {ADEID_SHORTNAME,   ADEDOFF_SHORTNAME,   ADEDLEN_INIT},
137     {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
138     {ADEID_PRIVDEV,     ADEDOFF_PRIVDEV,     ADEDLEN_INIT},
139     {ADEID_PRIVINO,     ADEDOFF_PRIVINO,     ADEDLEN_INIT},
140     {ADEID_PRIVSYN,     ADEDOFF_PRIVSYN,     ADEDLEN_INIT},
141     {ADEID_PRIVID,      ADEDOFF_PRIVID,      ADEDLEN_INIT},
142     {ADEID_RFORK,       ADEDOFF_RFORK_V2,    ADEDLEN_INIT},
143     {0, 0, 0}
144 };
145
146 /* Using Extended Attributes */
147 static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
148     {ADEID_FINDERI,    ADEDOFF_FINDERI_EA,    ADEDLEN_FINDERI},
149     {ADEID_COMMENT,    ADEDOFF_COMMENT_EA,    ADEDLEN_INIT},
150     {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI},
151     {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_EA,   ADEDLEN_AFPFILEI},
152     {0, 0, 0}
153 };
154
155 const char *adflags2logstr(int adflags)
156 {
157     int first = 1;
158     static char buf[64];
159
160     buf[0] = 0;
161
162     if (adflags & ADFLAGS_DF) {
163         strlcat(buf, "DF", 64);
164         first = 0;
165     }
166     if (adflags & ADFLAGS_RF) {
167         if (!first)
168             strlcat(buf, "|", 64);
169         strlcat(buf, "RF", 64);
170         first = 0;
171     }
172     if (adflags & ADFLAGS_HF) {
173         if (!first)
174             strlcat(buf, "|", 64);
175         strlcat(buf, "HF", 64);
176         first = 0;
177     }
178     if (adflags & ADFLAGS_NOHF) {
179         if (!first)
180             strlcat(buf, "|", 64);
181         strlcat(buf, "NOHF", 64);
182         first = 0;
183     }
184     if (adflags & ADFLAGS_DIR) {
185         if (!first)
186             strlcat(buf, "|", 64);
187         strlcat(buf, "DIR", 64);
188         first = 0;
189     }
190     if (adflags & ADFLAGS_CHECK_OF) {
191         if (!first)
192             strlcat(buf, "|", 64);
193         strlcat(buf, "OF", 64);
194         first = 0;
195     }
196     return buf;
197 }
198
199 static uint32_t get_eid(uint32_t eid)
200 {
201     if (eid <= 15)
202         return eid;
203     if (eid == AD_DEV)
204         return ADEID_PRIVDEV;
205     if (eid == AD_INO)
206         return ADEID_PRIVINO;
207     if (eid == AD_SYN)
208         return ADEID_PRIVSYN;
209     if (eid == AD_ID)
210         return ADEID_PRIVID;
211
212     return 0;
213 }
214
215 /* ----------------------------------- */
216 static int new_ad_header(const char *path, struct adouble *ad, int adflags)
217 {
218     const struct entry  *eid;
219     uint16_t            ashort;
220     struct stat         st;
221
222     ad->ad_magic = AD_MAGIC;
223     ad->ad_version = ad->ad_flags & 0x0f0000;
224     if (!ad->ad_version) {
225         ad->ad_version = AD_VERSION;
226     }
227
228
229     memset(ad->ad_data, 0, sizeof(ad->ad_data));
230
231     if (ad->ad_flags == AD_VERSION2)
232         eid = entry_order2;
233     else if (ad->ad_flags == AD_VERSION_EA)
234         eid = entry_order_ea;
235     else {
236         return -1;
237     }
238
239     while (eid->id) {
240         ad->ad_eid[eid->id].ade_off = eid->offset;
241         ad->ad_eid[eid->id].ade_len = eid->len;
242         eid++;
243     }
244
245     /* put something sane in the directory finderinfo */
246     if ((adflags & ADFLAGS_DIR)) {
247         /* set default view */
248         ashort = htons(FINDERINFO_CLOSEDVIEW);
249         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
250     } else {
251         /* set default creator/type fields */
252         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
253         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
254     }
255
256     /* make things invisible */
257     if ((ad->ad_options & ADVOL_INVDOTS) && (*path == '.')) {
258         ashort = htons(ATTRBIT_INVISIBLE);
259         ad_setattr(ad, ashort);
260         ashort = htons(FINDERINFO_INVISIBLE);
261         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
262     }
263
264     if (lstat(path, &st) < 0)
265         return -1;
266
267     /* put something sane in the date fields */
268     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
269     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
270     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
271     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
272     return 0;
273 }
274
275 /* -------------------------------------
276    read in the entries
277 */
278 static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
279 {
280     uint32_t   eid, len, off;
281     int        warning = 0;
282
283     /* now, read in the entry bits */
284     for (; nentries > 0; nentries-- ) {
285         memcpy(&eid, buf, sizeof( eid ));
286         eid = get_eid(ntohl(eid));
287         buf += sizeof( eid );
288         memcpy(&off, buf, sizeof( off ));
289         off = ntohl( off );
290         buf += sizeof( off );
291         memcpy(&len, buf, sizeof( len ));
292         len = ntohl( len );
293         buf += sizeof( len );
294
295         if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) &&
296             (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
297             ad->ad_eid[ eid ].ade_off = off;
298             ad->ad_eid[ eid ].ade_len = len;
299         } else if (!warning) {
300             warning = 1;
301             LOG(log_warning, logtype_default, "parse_entries: bogus eid: %d", eid);
302         }
303     }
304 }
305
306 /* this reads enough of the header so that we can figure out all of
307  * the entry lengths and offsets. once that's done, we just read/mmap
308  * the rest of the header in.
309  *
310  * NOTE: we're assuming that the resource fork is kept at the end of
311  *       the file. also, mmapping won't work for the hfs fs until it
312  *       understands how to mmap header files. */
313 static int ad_header_read(struct adouble *ad, struct stat *hst)
314 {
315     char                *buf = ad->ad_data;
316     uint16_t            nentries;
317     int                 len;
318     ssize_t             header_len;
319     struct stat         st;
320
321     /* read the header */
322     if ((header_len = adf_pread( ad->ad_mdp, buf, sizeof(ad->ad_data), 0)) < 0) {
323         return -1;
324     }
325     if (header_len < AD_HEADER_LEN) {
326         errno = EIO;
327         return -1;
328     }
329
330     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
331     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
332     ad->ad_magic = ntohl( ad->ad_magic );
333     ad->ad_version = ntohl( ad->ad_version );
334
335     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
336         LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
337         errno = EIO;
338         return -1;
339     }
340
341     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
342     nentries = ntohs( nentries );
343
344     /* read in all the entry headers. if we have more than the
345      * maximum, just hope that the rfork is specified early on. */
346     len = nentries*AD_ENTRY_LEN;
347
348     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
349         len = sizeof(ad->ad_data) - AD_HEADER_LEN;
350
351     buf += AD_HEADER_LEN;
352     if (len > header_len - AD_HEADER_LEN) {
353         LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
354         errno = EIO;
355         return -1;
356     }
357
358     /* figure out all of the entry offsets and lengths. if we aren't
359      * able to read a resource fork entry, bail. */
360     nentries = len / AD_ENTRY_LEN;
361     parse_entries(ad, buf, nentries);
362     if (!ad_getentryoff(ad, ADEID_RFORK)
363         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
364         ) {
365         LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset.");
366         errno = EIO;
367         return -1;
368     }
369
370     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
371         LOG(log_error, logtype_default, "ad_header_read: can't read in entries.");
372         errno = EIO;
373         return -1;
374     }
375
376     if (hst == NULL) {
377         hst = &st;
378         if (fstat(ad->ad_mdp->adf_fd, &st) < 0) {
379             return 1; /* fail silently */
380         }
381     }
382
383     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
384
385     return 0;
386 }
387
388 static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_)
389 {
390     uint16_t nentries;
391     int      len;
392     ssize_t  header_len;
393     char     *buf = ad->ad_data;
394
395     /* read the header */
396     if ((header_len = sys_fgetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA)) < 1) {
397         LOG(log_debug, logtype_default, "ad_header_read_ea: %s (%u)", strerror(errno), errno);
398         return -1;
399     }
400
401     if (header_len < AD_HEADER_LEN) {
402         LOG(log_error, logtype_default, "ad_header_read_ea: bogus AppleDouble header.");
403         errno = EIO;
404         return -1;
405     }
406
407     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
408     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
409
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_header_read_ea: wrong magic or version");
415         errno = EIO;
416         return -1;
417     }
418
419     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
420     nentries = ntohs( nentries );
421
422     /* Protect against bogus nentries */
423     len = nentries * AD_ENTRY_LEN;
424     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
425         len = sizeof(ad->ad_data) - AD_HEADER_LEN;
426     if (len > header_len - AD_HEADER_LEN) {
427         LOG(log_error, logtype_default, "ad_header_read_ea: can't read entry info.");
428         errno = EIO;
429         return -1;
430     }
431     nentries = len / AD_ENTRY_LEN;
432
433     /* Now parse entries */
434     parse_entries(ad, buf + AD_HEADER_LEN, nentries);
435     return 0;
436 }
437
438 static int ad_mkrf(const char *path)
439 {
440     char *slash;
441     /*
442      * Probably .AppleDouble doesn't exist, try to mkdir it.
443      */
444     if (NULL == ( slash = strrchr( path, '/' )) ) {
445         return -1;
446     }
447     *slash = '\0';
448     errno = 0;
449     if ( ad_mkdir( path, 0777 ) < 0 ) {
450         return -1;
451     }
452     *slash = '/';
453     return 0;
454 }
455
456 static int ad_mkrf_ea(const char *path _U_)
457 {
458     AFP_PANIC("ad_mkrf_ea: dont use");
459     return 0;
460 }
461
462 /* ----------------
463    if we are root change path user/ group
464    It can be a native function for BSD cf. FAQ.Q10
465    path:  pathname to chown
466    stbuf: parent directory inode
467
468    use fstat and fchown or lchown with linux?
469 */
470 #define EMULATE_SUIDDIR
471
472 static int ad_chown(const char *path, struct stat *stbuf)
473 {
474     int ret = 0;
475 #ifdef EMULATE_SUIDDIR
476     uid_t id;
477
478     if (default_uid != (uid_t)-1) {
479         /* we are root (admin) */
480         id = (default_uid)?default_uid:stbuf->st_uid;
481         ret = lchown( path, id, stbuf->st_gid );
482     }
483 #endif
484     return ret;
485 }
486
487 #define DEFMASK 07700   /* be conservative */
488
489 /* ----------------
490    return access right and inode of path parent directory
491 */
492 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
493 {
494     if (*mode == 0) {
495         return -1;
496     }
497     if (ad_stat(path, stbuf) != 0) {
498         *mode &= DEFMASK;
499         return -1;
500     }
501     *mode &= stbuf->st_mode;
502     return 0;
503 }
504
505 /* --------------------------- */
506 static int ad_header_upgrade(struct adouble *ad _U_, const char *name _U_)
507 {
508     return 0;
509 }
510
511 static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
512 {
513     AFP_PANIC("ad_header_upgrade_ea: dont use");
514     return 0;
515 }
516
517 /*!
518  * Error handling for adouble header(=metadata) file open error
519  *
520  * We're called because opening ADFLAGS_HF caused an error.
521  * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
522  * 2. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
523  *    ought to close it before returning with an error condition.
524  */
525 static int ad_error(struct adouble *ad, int adflags)
526 {
527     int err = errno;
528     if ((adflags & ADFLAGS_NOHF)) { /* 1 */
529         /* FIXME double check : set header offset ?*/
530         return 0;
531     }
532     if ((adflags & ADFLAGS_DF)) { /* 2 */
533         ad_close( ad, ADFLAGS_DF );
534         err = errno;
535     }
536     return -1 ;
537 }
538
539 /* Map ADFLAGS to open() flags */
540 static int ad2openflags(int adflags)
541 {
542     int oflags = 0;
543
544     if (adflags & ADFLAGS_RDWR)
545         oflags |= O_RDWR;
546     if (adflags & ADFLAGS_RDONLY)
547         oflags |= O_RDONLY;
548     if (adflags & ADFLAGS_CREATE)
549         oflags |= O_CREAT;
550     if (adflags & ADFLAGS_EXCL)
551         oflags |= O_EXCL;
552     if (adflags & ADFLAGS_TRUNC)
553         oflags |= O_TRUNC;
554
555     return oflags;
556 }
557
558 static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble *ad)
559 {
560     struct stat st_dir;
561     int         oflags;
562     mode_t      admode;
563     int         st_invalid = -1;
564     ssize_t     lsz;
565
566     LOG(log_debug, logtype_default, "ad_open_df(\"%s\", %s, %04o)",
567         fullpathname(path), mode);
568
569
570     if (ad_data_fileno(ad) != -1) {
571         /* the file is already open, but we want write access: */
572         if ((adflags & ADFLAGS_RDWR)
573             /* and it was denied the first time: */
574             && (ad->ad_data_fork.adf_flags & O_RDONLY)) {
575                 errno = EACCES;
576                 return -1;
577             }
578         /* it's not new anymore */
579         ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
580         ad->ad_data_fork.adf_refcount++;
581         return 0;
582     }
583
584     oflags = O_NOFOLLOW | ad2openflags(adflags);
585
586     admode = mode;
587     if ((adflags & ADFLAGS_CREATE)) {
588         st_invalid = ad_mode_st(path, &admode, &st_dir);
589         if ((ad->ad_options & ADVOL_UNIXPRIV))
590             admode = mode;
591     }
592
593     ad->ad_data_fork.adf_fd = open(path, oflags, admode);
594
595     if (ad->ad_data_fork.adf_fd == -1) {
596         if (errno != OPEN_NOFOLLOW_ERRNO)
597             return -1;
598
599         ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
600         if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
601             free(ad->ad_data_fork.adf_syml);
602             return -1;
603         }
604         ad->ad_data_fork.adf_syml[lsz] = 0;
605         ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
606     }
607
608     if (!st_invalid)
609         ad_chown(path, &st_dir); /* just created, set owner if admin (root) */
610
611     ad->ad_data_fork.adf_flags = oflags;
612     adf_lock_init(&ad->ad_data_fork);
613     ad->ad_data_fork.adf_refcount++;
614
615     return 0;
616 }
617
618 static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adouble *ad)
619 {
620     struct stat st_dir;
621     struct stat st_meta;
622     struct stat *pst = NULL;
623     const char  *ad_p;
624     int         oflags, nocreatflags;
625     mode_t      admode;
626     int         st_invalid = -1;
627
628     if (ad_meta_fileno(ad) != -1) {
629         /* the file is already open, but we want write access: */
630         if (!(adflags & ADFLAGS_RDONLY) &&
631             /* and it was already denied: */
632             !(ad->ad_mdp->adf_flags & O_RDWR)) {
633             errno = EACCES;
634             return -1;
635         }
636         ad_refresh(ad);
637         /* it's not new anymore */
638         ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
639         ad->ad_mdp->adf_refcount++;
640         return 0;
641     }
642
643     ad_p = ad->ad_ops->ad_path(path, adflags);
644     oflags = O_NOFOLLOW | ad2openflags(adflags);
645     nocreatflags = oflags & ~(O_CREAT | O_EXCL);
646     ad->ad_mdp->adf_fd = open(ad_p, nocreatflags);
647
648     if ( ad->ad_mdp->adf_fd < 0 ) {
649         if (!(errno == ENOENT && (oflags & O_CREAT)))
650             return ad_error(ad, adflags);
651         /*
652          * We're expecting to create a new adouble header file here
653          * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
654          */
655         LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
656             fullpathname(path));
657         admode = mode;
658         errno = 0;
659         st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
660         if ((ad->ad_options & ADVOL_UNIXPRIV))
661             admode = mode;
662         admode = ad_hf_mode(admode);
663         if (errno == ENOENT) {
664             if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
665                 return ad_error(ad, adflags);
666             }
667             admode = mode;
668             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
669             if ((ad->ad_options & ADVOL_UNIXPRIV))
670                 admode = mode;
671             admode = ad_hf_mode(admode);
672         }
673
674         /* retry with O_CREAT */
675         ad->ad_mdp->adf_fd = open(ad_p, oflags, admode);
676         if ( ad->ad_mdp->adf_fd < 0 )
677             return ad_error(ad, adflags);
678
679         ad->ad_mdp->adf_flags = oflags;
680         /* just created, set owner if admin owner (root) */
681         if (!st_invalid)
682             ad_chown(ad_p, &st_dir);
683     } else {
684         ad->ad_mdp->adf_flags = nocreatflags;
685         if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
686             /* for 0 length files, treat them as new. */
687             ad->ad_mdp->adf_flags |= O_TRUNC;
688         } else {
689             /* we have valid data in st_meta stat structure, reused it in ad_header_read */
690             pst = &st_meta;
691         }
692     }
693
694     ad->ad_mdp->adf_refcount = 1;
695     adf_lock_init(ad->ad_mdp);
696
697     if ((ad->ad_mdp->adf_flags & ( O_TRUNC | O_CREAT ))) {
698         /* This is a new adouble header file, create it */
699         if (new_ad_header(path, ad, adflags) < 0) {
700             int err = errno;
701             /* the file is already deleted, perm, whatever, so return an error */
702             ad_close(ad, adflags);
703             errno = err;
704             return -1;
705         }
706         ad_flush(ad);
707     } else {
708         /* Read the adouble header in and parse it.*/
709         if (ad->ad_ops->ad_header_read( ad , pst) < 0
710             || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) {
711             int err = errno;
712             ad_close(ad, adflags);
713             errno = err;
714             return -1;
715         }
716     }
717
718     return 0;
719 }
720
721 static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
722 {
723     ssize_t rforklen;
724     int oflags = O_NOFOLLOW;
725
726     oflags = ad2openflags(adflags) & ~(O_CREAT | O_TRUNC);
727
728     if (ad_meta_fileno(ad) == -1) {
729         if ((ad_meta_fileno(ad) = open(path, oflags)) == -1)
730             goto error;
731         ad->ad_mdp->adf_flags = oflags;
732         ad->ad_mdp->adf_refcount = 1;
733         adf_lock_init(ad->ad_mdp);
734     }
735
736     /* Read the adouble header in and parse it.*/
737     if (ad->ad_ops->ad_header_read(ad, NULL) != 0) {
738         if (!(adflags & ADFLAGS_CREATE))
739             goto error;
740
741         /* It doesnt exist, EPERM or another error */
742         if (!(errno == ENOATTR || errno == ENOENT)) {
743             LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
744             goto error;
745         }
746
747         /* Create one */
748         if (new_ad_header(path, ad, adflags) < 0) {
749             LOG(log_error, logtype_default, "ad_open_hf_ea: can't create new header: %s",
750                 fullpathname(path));
751             goto error;
752         }
753         ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
754         ad_flush(ad);
755         LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
756     }
757
758     ad->ad_mdp->adf_refcount++;
759
760     if ((rforklen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) > 0)
761         ad->ad_rlen = rforklen;
762
763     return 0;
764
765 error:
766     if (ad_meta_fileno(ad) != -1) {
767         close(ad_meta_fileno(ad));
768         ad_meta_fileno(ad) = -1;
769     }
770     return ad_error(ad, adflags);
771 }
772
773 static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *ad)
774 {
775     int ret = 0;
776
777     LOG(log_debug, logtype_default, "ad_open_hf(\"%s\", %04o)", path, mode);
778
779     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
780     ad->ad_rlen = 0;
781
782     switch (ad->ad_flags) {
783     case AD_VERSION2:
784         ret = ad_open_hf_v2(path, adflags, mode, ad);
785         break;
786     case AD_VERSION_EA:
787         ret = ad_open_hf_ea(path, adflags, mode, ad);
788         break;
789     default:
790         ret = -1;
791         break;
792     }
793
794     return ret;
795 }
796
797 /*!
798  * Open ressource fork
799  *
800  * Only for adouble:ea, a nullop otherwise because adouble:v2 has the ressource fork as part
801  * of the adouble file which is openend by ADFLAGS_HF.
802  */
803 static int ad_open_rf(const char *path, int adflags, int mode, struct adouble *ad)
804 {
805     int ret = 0;
806
807     if (ad->ad_flags != AD_VERSION_EA)
808         return 0;
809
810     LOG(log_debug, logtype_default, "ad_open_rf(\"%s\", %04o)",
811         path, mode);
812
813     if ((ad->ad_rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) <= 0) {
814         switch (errno) {
815         case ENOATTR:
816             ad->ad_rlen = 0;
817             break;
818         default:
819             LOG(log_warning, logtype_default, "ad_open_rf(\"%s\"): %s",
820                 fullpathname(path), strerror(errno));
821             ret = -1;
822             goto exit;
823         }
824     }
825
826     /* Round up and allocate buffer */
827     size_t roundup = ((ad->ad_rlen / RFORK_EA_ALLOCSIZE) + 1) * RFORK_EA_ALLOCSIZE;
828     if ((ad->ad_resforkbuf = malloc(roundup)) == NULL) {
829         ret = -1;
830         goto exit;
831     }
832
833     ad->ad_resforkbufsize = roundup;
834
835     /* Read the EA into the buffer */
836     if (ad->ad_rlen > 0) {
837         if (sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen) == -1) {
838             ret = -1;
839             goto exit;
840         }       
841     }
842
843 exit:
844     if (ret != 0) {
845         free(ad->ad_resforkbuf);
846         ad->ad_resforkbuf = NULL;
847         ad->ad_rlen = 0;
848         ad->ad_resforkbufsize = 0;
849     }
850
851     return ret;
852 }
853
854 /***********************************************************************************
855  * API functions
856  ********************************************************************************* */
857
858 const char *ad_path_ea( const char *path, int adflags _U_)
859 {
860     return path;
861 }
862
863 /*
864  * Put the .AppleDouble where it needs to be:
865  *
866  *      /   a/.AppleDouble/b
867  *  a/b
868  *      \   b/.AppleDouble/.Parent
869  *
870  * FIXME: should do something for pathname > MAXPATHLEN
871  */
872 const char *ad_path( const char *path, int adflags)
873 {
874     static char pathbuf[ MAXPATHLEN + 1];
875     const char *slash;
876     size_t  l ;
877
878     if ( adflags & ADFLAGS_DIR ) {
879         l = strlcpy( pathbuf, path, sizeof(pathbuf));
880
881         if ( l && l < MAXPATHLEN) {
882             pathbuf[l++] = '/';
883         }
884         strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
885     } else {
886         if (NULL != ( slash = strrchr( path, '/' )) ) {
887             slash++;
888             l = slash - path;
889             /* XXX we must return NULL here and test in the caller */
890             if (l > MAXPATHLEN)
891                 l = MAXPATHLEN;
892             memcpy( pathbuf, path, l);
893         } else {
894             l = 0;
895             slash = path;
896         }
897         l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
898         strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
899     }
900
901     return( pathbuf );
902 }
903
904 /* -------------------------
905  * Support inherited protection modes for AppleDouble files.  The supplied
906  * mode is ANDed with the parent directory's mask value in lieu of "umask",
907  * and that value is returned.
908  */
909 char *ad_dir(const char *path)
910 {
911     static char     modebuf[ MAXPATHLEN + 1];
912     char        *slash;
913     /*
914      * For a path with directories in it, remove the final component
915      * (path or subdirectory name) to get the name we want to stat.
916      * For a path which is just a filename, use "." instead.
917      */
918     slash = strrchr( path, '/' );
919     if (slash) {
920         size_t len;
921
922         len = slash - path;
923         if (len >= MAXPATHLEN) {
924             errno = ENAMETOOLONG;
925             return NULL;  /* can't do it */
926         }
927         memcpy( modebuf, path, len );
928         modebuf[len] = '\0';
929         /* is last char a '/' ? */
930         if (slash[1] == 0) {
931             slash = modebuf+ len;
932             /* remove them */
933             while (modebuf < slash && slash[-1] == '/') {
934                 --slash;
935             }
936             if (modebuf == slash) {
937                 goto use_cur;
938             }
939             *slash = '\0';
940             while (modebuf < slash && *slash != '/') {
941                 --slash;
942             }
943             if (modebuf == slash) {
944                 goto use_cur;
945             }
946             *slash = '\0';      /* remove pathname component */
947         }
948         return modebuf;
949     }
950 use_cur:
951     modebuf[0] = '.';   /* use current directory */
952     modebuf[1] = '\0';
953     return modebuf;
954 }
955
956 int ad_setfuid(const uid_t id)
957 {
958     default_uid = id;
959     return 0;
960 }
961
962 /* ---------------- */
963 uid_t ad_getfuid(void)
964 {
965     return default_uid;
966 }
967
968 /* ----------------
969    stat path parent directory
970 */
971 int ad_stat(const char *path, struct stat *stbuf)
972 {
973     char *p;
974
975     p = ad_dir(path);
976     if (!p)
977         return -1;
978     return lstat( p, stbuf );
979 }
980
981 /* ----------------
982    return access right of path parent directory
983 */
984 int ad_mode( const char *path, int mode)
985 {
986     struct stat     stbuf;
987     ad_mode_st(path, &mode, &stbuf);
988     return mode;
989 }
990
991 /*
992  * Use mkdir() with mode bits taken from ad_mode().
993  */
994 int ad_mkdir( const char *path, int mode)
995 {
996     int ret;
997     int st_invalid;
998     struct stat stbuf;
999
1000     LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
1001         path, mode, getcwdpath());
1002
1003     st_invalid = ad_mode_st(path, &mode, &stbuf);
1004     ret = mkdir( path, mode );
1005     if (ret || st_invalid)
1006         return ret;
1007     ad_chown(path, &stbuf);
1008
1009     return ret;
1010 }
1011
1012 void ad_init(struct adouble *ad, int flags, int options)
1013 {
1014     switch (flags) {
1015     case AD_VERSION2:
1016         ad->ad_ops = &ad_adouble;
1017         ad->ad_rfp = &ad->ad_resource_fork;
1018         ad->ad_mdp = &ad->ad_resource_fork;
1019         break;
1020     case AD_VERSION_EA:
1021         ad->ad_ops = &ad_adouble_ea;
1022         ad->ad_rfp = &ad->ad_data_fork;
1023         ad->ad_mdp = &ad->ad_data_fork;
1024         break;
1025     default:
1026         LOG(log_error, logtype_default, "ad_init: unknown AD version");
1027         errno = EIO;
1028         return;
1029     }
1030
1031     ad->ad_flags = flags;
1032     ad->ad_options = options;
1033     ad_data_fileno(ad) = -1;
1034     ad_reso_fileno(ad) = -1;
1035     ad_meta_fileno(ad) = -1;
1036     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1037     ad->ad_rlen = 0;
1038     ad->ad_refcount = 1;
1039     ad->ad_open_forks = 0;
1040     ad->ad_resource_fork.adf_refcount = 0;
1041     ad->ad_data_fork.adf_refcount = 0;
1042     ad->ad_data_fork.adf_syml=0;
1043     ad->ad_inited = 0;
1044 }
1045
1046 /*!
1047  * Open data-, metadata(header)- or ressource fork
1048  *
1049  * ad_open(struct adouble *ad, const char *path, int adflags, int flags)
1050  * ad_open(struct adouble *ad, const char *path, int adflags, int flags, mode_t mode)
1051  *
1052  * You must call ad_init() before ad_open, usually you'll just call it like this: \n
1053  * @code
1054  *      struct adoube ad;
1055  *      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1056  * @endcode
1057  *
1058  * Open a files data fork, metadata fork or ressource fork.
1059  *
1060  * @param ad        (rw) pointer to struct adouble
1061  * @param path      (r)  Path to file or directory
1062  * @param adflags   (r)  ADFLAGS_DF:        open data fork \n
1063  *                       ADFLAGS_RF:        open ressource fork \n
1064  *                       ADFLAGS_HF:        open header (metadata) file \n
1065  *                       ADFLAGS_NOHF:      it's not an error if header file couldn't be created \n
1066  *                       ADFLAGS_DIR:       if path is a directory you MUST or ADFLAGS_DIR to adflags \n
1067  * The open mode flags (rw vs ro) have to take into account all the following requirements:
1068  * - we remember open fds for files because me must avoid a single close releasing fcntl locks for other
1069  *   fds of the same file
1070  * - a file may be opened first ro, then rw and theres no way to upgrade this -> fork.c always opens rw
1071  *                       ADFLAGS_CHECK_OF:  check for open forks from us and other afpd's
1072  * @param mode      (r)  mode used with O_CREATE
1073  *
1074  * @returns 0 on success, any other value indicates an error
1075  */
1076 int ad_open(struct adouble *ad, const char *path, int adflags, ...)
1077 {
1078     EC_INIT;
1079     va_list args;
1080     mode_t mode = 0;
1081
1082     LOG(log_debug, logtype_default, "ad_open(\"%s\", %s, %s)",
1083         fullpathname(path), adflags2logstr(adflags));
1084
1085     if (ad->ad_inited != AD_INITED) {
1086         ad->ad_adflags = adflags;
1087     } else {
1088         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1089         if (ad->ad_resource_fork.adf_refcount > 0)
1090             ad->ad_open_forks |= ATTRBIT_ROPEN;
1091     }
1092
1093     va_start(args, adflags);
1094     if (adflags & ADFLAGS_CREATE)
1095         mode = va_arg(args, mode_t);
1096     va_end(args);
1097
1098     if ((adflags & ADFLAGS_DF) && !(ad->ad_adflags & ADFLAGS_DF)) { /* 3 */
1099         EC_ZERO( ad_open_df(path, adflags, mode, ad) );
1100         ad->ad_adflags |= ADFLAGS_DF;
1101     }
1102
1103     if ((adflags & ADFLAGS_HF) && !(ad->ad_adflags & ADFLAGS_HF)) { /* 3 */
1104         EC_ZERO( ad_open_hf(path, adflags, mode, ad) );
1105         ad->ad_adflags |= ADFLAGS_HF;
1106     }
1107
1108     if ((adflags & ADFLAGS_RF) && !(ad->ad_adflags & ADFLAGS_RF)) { /* 3 */
1109         EC_ZERO( ad_open_rf(path, adflags, mode, ad) );
1110         ad->ad_adflags |= ADFLAGS_RF;
1111     }
1112
1113 EC_CLEANUP:
1114     return ret;
1115 }
1116
1117 /*!
1118  * @brief open metadata, possibly as root
1119  *
1120  * Return only metadata but try very hard ie at first try as user, then try as root.
1121  *
1122  * @param name  name of file/dir
1123  * @param flags ADFLAGS_DIR: name is a directory \n
1124  *              ADFLAGS_CHECK_OF: test if name is open by us or another afpd process
1125  *
1126  * @param adp   pointer to struct adouble
1127  */
1128 int ad_metadata(const char *name, int flags, struct adouble *adp)
1129 {
1130     uid_t uid;
1131     int   ret, err, oflags;
1132
1133     oflags = (flags & ADFLAGS_DIR) | ADFLAGS_HF | ADFLAGS_RDONLY;
1134
1135 //    if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) {
1136     if ((ret = ad_open(adp, name, oflags, 0)) < 0 && errno == EACCES) {
1137         uid = geteuid();
1138         if (seteuid(0)) {
1139             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1140             errno = EACCES;
1141             return -1;
1142         }
1143         /* we are root open read only */
1144 //        ret = ad_open(adp, name, oflags);
1145         ret = ad_open(adp, name, oflags, 0);
1146         err = errno;
1147         if ( seteuid(uid) < 0) {
1148             LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1149             exit(EXITERR_SYS);
1150         }
1151         errno = err;
1152     }
1153
1154     if ((ret == 0) && (ADFLAGS_CHECK_OF & flags)) {
1155         /*
1156           we need to check if the file is open by another process.
1157           it's slow so we only do it if we have to:
1158           - it's requested.
1159           - we don't already have the answer!
1160         */
1161         adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks);
1162     }
1163     return ret;
1164 }
1165
1166 /*
1167  * @brief openat like wrapper for ad_metadata
1168  */
1169 int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
1170 {
1171     int ret = 0;
1172     int cwdfd = -1;
1173
1174     if (dirfd != -1) {
1175         if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
1176             ret = -1;
1177             goto exit;
1178         }
1179     }
1180
1181     if (ad_metadata(name, flags, adp) < 0) {
1182         ret = -1;
1183         goto exit;
1184     }
1185
1186     if (dirfd != -1) {
1187         if (fchdir(cwdfd) != 0) {
1188             LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
1189             exit(EXITERR_SYS);
1190         }
1191     }
1192
1193 exit:
1194     if (cwdfd != -1)
1195         close(cwdfd);
1196
1197     return ret;
1198
1199 }
1200
1201 int ad_refresh(struct adouble *ad)
1202 {
1203
1204     if (ad_meta_fileno(ad) == -1)
1205         return -1;
1206
1207     return ad->ad_ops->ad_header_read(ad, NULL);
1208 }
1209
1210 int ad_openat(struct adouble  *ad,
1211               int dirfd,  /* dir fd openat like */
1212               const char *path,
1213               int adflags, ...)
1214 {
1215     EC_INIT;
1216     int cwdfd = -1;
1217     va_list args;
1218     mode_t mode;
1219
1220     if (dirfd != -1) {
1221         if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0))
1222             EC_FAIL;
1223     }
1224
1225     va_start(args, adflags);
1226     if (adflags & ADFLAGS_CREATE)
1227         mode = va_arg(args, mode_t);
1228     va_end(args);
1229
1230     EC_NEG1( ad_open(ad, path, adflags, mode) );
1231
1232     if (dirfd != -1) {
1233         if (fchdir(cwdfd) != 0) {
1234             AFP_PANIC("ad_openat: cant chdir back");
1235         }
1236     }
1237
1238 EC_CLEANUP:
1239     if (cwdfd != -1)
1240         close(cwdfd);
1241
1242     return ret;
1243 }