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
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.
18 * Research Systems Unix Group
19 * The University of Michigan
21 * 535 W. William Street
24 * netatalk@itd.umich.edu
30 * Part of Netatalk's AppleDouble implementatation
31 * @sa include/atalk/adouble.h
36 #endif /* HAVE_CONFIG_H */
39 #include <sys/param.h>
43 #include <arpa/inet.h>
45 #include <atalk/logger.h>
46 #include <atalk/adouble.h>
47 #include <atalk/util.h>
48 #include <atalk/unix.h>
50 #include <atalk/bstrlib.h>
51 #include <atalk/bstradd.h>
52 #include <atalk/compat.h>
53 #include <atalk/errchk.h>
54 #include <atalk/volume.h>
58 #define ADEDOFF_MAGIC (0)
59 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
60 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
61 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
63 /* initial lengths of some of the fields */
64 #define ADEDLEN_INIT 0
66 /* i stick things in a slightly different order than their eid order in
67 * case i ever want to separate RootInfo behaviour from the rest of the
71 #define ADEDOFF_NAME_V2 (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
72 #define ADEDOFF_COMMENT_V2 (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
73 #define ADEDOFF_FILEDATESI (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
74 #define ADEDOFF_FINDERI_V2 (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
75 #define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
76 #define ADEDOFF_AFPFILEI (ADEDOFF_DID + ADEDLEN_DID)
77 #define ADEDOFF_SHORTNAME (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
78 #define ADEDOFF_PRODOSFILEI (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
79 #define ADEDOFF_PRIVDEV (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
80 #define ADEDOFF_PRIVINO (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
81 #define ADEDOFF_PRIVSYN (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
82 #define ADEDOFF_PRIVID (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
83 #define ADEDOFF_RFORK_V2 (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
86 #define ADEDOFF_FINDERI_EA (AD_HEADER_LEN + ADEID_NUM_EA * AD_ENTRY_LEN)
87 #define ADEDOFF_COMMENT_EA (ADEDOFF_FINDERI_EA + ADEDLEN_FINDERI)
88 #define ADEDOFF_FILEDATESI_EA (ADEDOFF_COMMENT_EA + ADEDLEN_COMMENT)
89 #define ADEDOFF_AFPFILEI_EA (ADEDOFF_FILEDATESI_EA + ADEDLEN_FILEDATESI)
90 #define ADEDOFF_PRIVDEV_EA (ADEDOFF_AFPFILEI_EA + ADEDLEN_AFPFILEI)
91 #define ADEDOFF_PRIVINO_EA (ADEDOFF_PRIVDEV_EA + ADEDLEN_PRIVDEV)
92 #define ADEDOFF_PRIVSYN_EA (ADEDOFF_PRIVINO_EA + ADEDLEN_PRIVINO)
93 #define ADEDOFF_PRIVID_EA (ADEDOFF_PRIVSYN_EA + ADEDLEN_PRIVSYN)
95 /* this is to prevent changing timezones from causing problems with
96 localtime volumes. the screw-up is 30 years. we use a delta of 5 years */
97 #define TIMEWARP_DELTA 157680000
100 uint32_t id, offset, len;
103 /* --------------------------- */
104 static uid_t default_uid = -1;
106 /* Forward declarations */
107 static int ad_mkrf(const char *path);
108 static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst);
109 static int ad_header_upgrade(struct adouble *ad, const char *name);
112 static int ad_mkrf_ea(const char *path);
114 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst);
115 static int ad_header_upgrade_ea(struct adouble *ad, const char *name);
116 off_t ad_reso_size(const char *path, int adflags, struct adouble *ad);
117 static int ad_mkrf_osx(const char *path);
120 static struct adouble_fops ad_adouble = {
123 &ad_rebuild_adouble_header_v2,
128 static struct adouble_fops ad_adouble_ea = {
136 &ad_rebuild_adouble_header_ea,
138 &ad_header_upgrade_ea,
141 static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
142 {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
143 {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
144 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
145 {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
146 {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
147 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
148 {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
149 {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
150 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT},
151 {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT},
152 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT},
153 {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT},
154 {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
158 /* Using Extended Attributes */
159 static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
160 {ADEID_FINDERI, ADEDOFF_FINDERI_EA, ADEDLEN_FINDERI},
161 {ADEID_COMMENT, ADEDOFF_COMMENT_EA, ADEDLEN_INIT},
162 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI},
163 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_EA, ADEDLEN_AFPFILEI},
164 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_EA, ADEDLEN_INIT},
165 {ADEID_PRIVINO, ADEDOFF_PRIVINO_EA, ADEDLEN_INIT},
166 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_EA, ADEDLEN_INIT},
167 {ADEID_PRIVID, ADEDOFF_PRIVID_EA, ADEDLEN_INIT},
171 #define ADFLAGS2LOGSTRBUFSIZ 128
172 const char *adflags2logstr(int adflags)
175 static char buf[ADFLAGS2LOGSTRBUFSIZ];
179 if (adflags & ADFLAGS_DF) {
180 strlcat(buf, "DF", ADFLAGS2LOGSTRBUFSIZ);
183 if (adflags & ADFLAGS_RF) {
185 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
186 strlcat(buf, "RF", ADFLAGS2LOGSTRBUFSIZ);
189 if (adflags & ADFLAGS_NORF) {
191 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
192 strlcat(buf, "NORF", ADFLAGS2LOGSTRBUFSIZ);
195 if (adflags & ADFLAGS_HF) {
197 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
198 strlcat(buf, "HF", ADFLAGS2LOGSTRBUFSIZ);
201 if (adflags & ADFLAGS_NOHF) {
203 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
204 strlcat(buf, "NOHF", ADFLAGS2LOGSTRBUFSIZ);
207 if (adflags & ADFLAGS_DIR) {
209 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
210 strlcat(buf, "DIR", ADFLAGS2LOGSTRBUFSIZ);
213 if (adflags & ADFLAGS_CHECK_OF) {
215 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
216 strlcat(buf, "OF", ADFLAGS2LOGSTRBUFSIZ);
219 if (adflags & ADFLAGS_SETSHRMD) {
221 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
222 strlcat(buf, "SHRMD", ADFLAGS2LOGSTRBUFSIZ);
225 if (adflags & ADFLAGS_RDWR) {
227 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
228 strlcat(buf, "O_RDWR", ADFLAGS2LOGSTRBUFSIZ);
231 if (adflags & ADFLAGS_RDONLY) {
233 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
234 strlcat(buf, "O_RDONLY", ADFLAGS2LOGSTRBUFSIZ);
237 if (adflags & ADFLAGS_CREATE) {
239 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
240 strlcat(buf, "O_CREAT", ADFLAGS2LOGSTRBUFSIZ);
243 if (adflags & ADFLAGS_EXCL) {
245 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
246 strlcat(buf, "O_EXCL", ADFLAGS2LOGSTRBUFSIZ);
249 if (adflags & ADFLAGS_TRUNC) {
251 strlcat(buf, "|", ADFLAGS2LOGSTRBUFSIZ);
252 strlcat(buf, "O_TRUNC", ADFLAGS2LOGSTRBUFSIZ);
259 #define OPENFLAGS2LOGSTRBUFSIZ 128
260 const char *openflags2logstr(int oflags)
263 static char buf[OPENFLAGS2LOGSTRBUFSIZ];
267 if ((oflags & O_RDONLY) || (oflags == O_RDONLY)) {
268 strlcat(buf, "O_RDONLY", OPENFLAGS2LOGSTRBUFSIZ);
271 if (oflags & O_RDWR) {
273 strlcat(buf, "|", OPENFLAGS2LOGSTRBUFSIZ);
274 strlcat(buf, "O_RDWR", OPENFLAGS2LOGSTRBUFSIZ);
277 if (oflags & O_CREAT) {
279 strlcat(buf, "|", OPENFLAGS2LOGSTRBUFSIZ);
280 strlcat(buf, "O_CREAT", OPENFLAGS2LOGSTRBUFSIZ);
283 if (oflags & O_TRUNC) {
285 strlcat(buf, "|", OPENFLAGS2LOGSTRBUFSIZ);
286 strlcat(buf, "O_TRUNC", OPENFLAGS2LOGSTRBUFSIZ);
289 if (oflags & O_EXCL) {
291 strlcat(buf, "|", OPENFLAGS2LOGSTRBUFSIZ);
292 strlcat(buf, "O_EXCL", OPENFLAGS2LOGSTRBUFSIZ);
298 static uint32_t get_eid(uint32_t eid)
303 return ADEID_PRIVDEV;
305 return ADEID_PRIVINO;
307 return ADEID_PRIVSYN;
316 * Initialize offset pointers
318 int ad_init_offsets(struct adouble *ad)
320 const struct entry *eid;
322 if (ad->ad_magic == AD_MAGIC)
325 ad->ad_magic = AD_MAGIC;
326 ad->ad_version = ad->ad_vers & 0x0f0000;
327 if (!ad->ad_version) {
328 ad->ad_version = AD_VERSION;
331 memset(ad->ad_data, 0, sizeof(ad->ad_data));
333 if (ad->ad_vers == AD_VERSION2)
335 else if (ad->ad_vers == AD_VERSION_EA)
336 eid = entry_order_ea;
341 ad->ad_eid[eid->id].ade_off = eid->offset;
342 ad->ad_eid[eid->id].ade_len = eid->len;
347 * Ensure the resource fork offset is always set
350 if (ad->ad_vers == AD_VERSION_EA)
351 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_OSX);
357 /* ----------------------------------- */
358 static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp, int adflags)
360 const struct entry *eid;
364 LOG(log_debug, logtype_ad, "new_ad_header(\"%s\")", path);
366 if (ad_init_offsets(ad) != 0)
369 /* set default creator/type fields */
370 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
371 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
373 /* make things invisible */
374 if ((ad->ad_options & ADVOL_INVDOTS)
376 && !((adflags & ADFLAGS_DIR) && (path[1] == 0))
378 ashort = htons(ATTRBIT_INVISIBLE);
379 ad_setattr(ad, ashort);
380 ashort = htons(FINDERINFO_INVISIBLE);
381 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
384 /* put something sane in the date fields */
387 if (lstat(path, &st) != 0)
390 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
391 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
392 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
393 ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
399 * Read an AppleDouble buffer, returns 0 on success, -1 if an entry was malformatted
401 static int parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
403 uint32_t eid, len, off;
406 /* now, read in the entry bits */
407 for (; nentries > 0; nentries-- ) {
408 memcpy(&eid, buf, sizeof( eid ));
409 eid = get_eid(ntohl(eid));
410 buf += sizeof( eid );
411 memcpy(&off, buf, sizeof( off ));
413 buf += sizeof( off );
414 memcpy(&len, buf, sizeof( len ));
416 buf += sizeof( len );
418 ad->ad_eid[eid].ade_off = off;
419 ad->ad_eid[eid].ade_len = len;
423 || off >= sizeof(ad->ad_data)
424 || ((eid != ADEID_RFORK) && (off + len > sizeof(ad->ad_data))))
427 LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
428 (uint)eid, (uint)off, (uint)len);
435 /* this reads enough of the header so that we can figure out all of
436 * the entry lengths and offsets. once that's done, we just read/mmap
437 * the rest of the header in.
439 * NOTE: we're assuming that the resource fork is kept at the end of
440 * the file. also, mmapping won't work for the hfs fs until it
441 * understands how to mmap header files. */
442 static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst)
444 char *buf = ad->ad_data;
450 /* read the header */
451 if ((header_len = adf_pread( ad->ad_mdp, buf, AD_DATASZ2, 0)) < 0) {
454 if (header_len < AD_HEADER_LEN) {
459 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
460 memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
461 ad->ad_magic = ntohl( ad->ad_magic );
462 ad->ad_version = ntohl( ad->ad_version );
464 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
465 LOG(log_error, logtype_ad, "ad_open: can't parse AppleDouble header.");
470 memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
471 nentries = ntohs( nentries );
473 /* read in all the entry headers. if we have more than the
474 * maximum, just hope that the rfork is specified early on. */
475 len = nentries*AD_ENTRY_LEN;
477 if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
478 len = sizeof(ad->ad_data) - AD_HEADER_LEN;
480 buf += AD_HEADER_LEN;
481 if (len > header_len - AD_HEADER_LEN) {
482 LOG(log_error, logtype_ad, "ad_header_read: can't read entry info.");
487 /* figure out all of the entry offsets and lengths. if we aren't
488 * able to read a resource fork entry, bail. */
489 nentries = len / AD_ENTRY_LEN;
490 if (parse_entries(ad, buf, nentries) != 0) {
491 LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
492 path ? fullpathname(path) : "");
496 if (!ad_getentryoff(ad, ADEID_RFORK)
497 || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
499 LOG(log_error, logtype_ad, "ad_header_read: problem with rfork entry offset.");
504 if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
505 LOG(log_error, logtype_ad, "ad_header_read: can't read in entries.");
512 if (fstat(ad->ad_mdp->adf_fd, &st) < 0) {
513 return 1; /* fail silently */
517 ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
522 /* error here means it's not ad ._ adouble:osx file and thus we return 1 */
523 int ad_valid_header_osx(const char *path)
527 struct adouble adosx;
528 char *buf = &adosx.ad_data[0];
531 LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): BEGIN", fullpathname(path));
533 EC_NEG1( fd = open(path, O_RDONLY) );
535 /* read the header */
536 EC_NEG1( header_len = read(fd, buf, AD_DATASZ_OSX) );
538 if (header_len < AD_HEADER_LEN)
541 memcpy(&adosx.ad_magic, buf, sizeof(adosx.ad_magic));
542 memcpy(&adosx.ad_version, buf + ADEDOFF_VERSION, sizeof(adosx.ad_version));
543 adosx.ad_magic = ntohl(adosx.ad_magic);
544 adosx.ad_version = ntohl(adosx.ad_version);
546 if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
547 LOG(log_warning, logtype_ad, "ad_valid_header_osx(\"%s\"): not an adouble:osx file", fullpathname(path));
551 if (strncmp(buf + ADEDOFF_FILLER,
553 strlen(AD_FILLER_NETATALK)) != 0)
555 * It's a split fork created by OS X, it's not our "own" ._ file
556 * and thus not a valid header in this context.
557 * We allow enumeration and access.
562 LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): END: %d", fullpathname(path), ret);
571 * Convert from Apple's ._ file to Netatalk
573 * Apple's AppleDouble may contain a FinderInfo entry longer then 32 bytes
574 * containing packed xattrs. Netatalk can't deal with that, so we
575 * simply discard the packed xattrs.
577 * As we call ad_open() which might result in a recursion, just to be sure
578 * use static variable in_conversion to check for that.
580 * Returns -1 in case an error occured, 0 if no conversion was done, 1 otherwise
582 static int ad_convert_osx(const char *path, struct adouble *ad)
585 static bool in_conversion = false;
587 int finderlen = ad_getentrylen(ad, ADEID_FINDERI);
590 if (in_conversion || finderlen == ADEDLEN_FINDERI)
592 in_conversion = true;
594 LOG(log_debug, logtype_ad, "Converting OS X AppleDouble %s, FinderInfo length: %d",
595 fullpathname(path), finderlen);
597 origlen = ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK);
599 map = mmap(NULL, origlen, PROT_READ | PROT_WRITE, MAP_SHARED, ad_reso_fileno(ad), 0);
600 if (map == MAP_FAILED) {
601 LOG(log_error, logtype_ad, "mmap AppleDouble: %s\n", strerror(errno));
605 memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
606 map + ad_getentryoff(ad, ADEID_RFORK),
607 ad_getentrylen(ad, ADEID_RFORK));
609 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
610 ad->ad_rlen = ad_getentrylen(ad, ADEID_RFORK);
611 ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
613 EC_ZERO_LOG( ftruncate(ad_reso_fileno(ad),
614 ad_getentryoff(ad, ADEID_RFORK)
615 + ad_getentrylen(ad, ADEID_RFORK)) );
617 (void)ad_rebuild_adouble_header_osx(ad, map);
618 munmap(map, origlen);
620 /* Create a metadata EA if one doesn't exit */
621 if (strlen(path) < 3)
624 ad_init_old(&adea, AD_VERSION_EA, ad->ad_options);
626 if (ad_open(&adea, path + 2, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
627 LOG(log_error, logtype_ad, "create metadata: %s\n", strerror(errno));
630 if (adea.ad_mdp->adf_flags & O_CREAT) {
631 memcpy(ad_entry(&adea, ADEID_FINDERI),
632 ad_entry(ad, ADEID_FINDERI),
636 ad_close(&adea, ADFLAGS_HF);
639 in_conversion = false;
645 /* Read an ._ file, only uses the resofork, finderinfo is taken from EA */
646 static int ad_header_read_osx(const char *path, struct adouble *ad, const struct stat *hst)
649 struct adouble adosx;
658 LOG(log_debug, logtype_ad, "ad_header_read_osx: %s", path ? fullpathname(path) : "");
659 ad_init_old(&adosx, AD_VERSION_EA, ad->ad_options);
660 buf = &adosx.ad_data[0];
661 memset(buf, 0, sizeof(adosx.ad_data));
662 adosx.ad_rfp->adf_fd = ad_reso_fileno(ad);
664 /* read the header */
665 EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
667 if (header_len < AD_HEADER_LEN) {
672 memcpy(&adosx.ad_magic, buf, sizeof(adosx.ad_magic));
673 memcpy(&adosx.ad_version, buf + ADEDOFF_VERSION, sizeof(adosx.ad_version));
674 adosx.ad_magic = ntohl(adosx.ad_magic);
675 adosx.ad_version = ntohl(adosx.ad_version);
677 if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
678 LOG(log_error, logtype_ad, "ad_header_read_osx: can't parse AppleDouble header");
683 memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
684 nentries = ntohs(nentries);
685 len = nentries * AD_ENTRY_LEN;
687 if (len + AD_HEADER_LEN > sizeof(adosx.ad_data))
688 len = sizeof(adosx.ad_data) - AD_HEADER_LEN;
690 buf += AD_HEADER_LEN;
691 if (len > header_len - AD_HEADER_LEN) {
692 LOG(log_error, logtype_ad, "ad_header_read_osx: can't read entry info.");
697 nentries = len / AD_ENTRY_LEN;
698 if (parse_entries(&adosx, buf, nentries) != 0) {
699 LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
700 path ? fullpathname(path) : "");
703 if (ad_getentrylen(&adosx, ADEID_FINDERI) != ADEDLEN_FINDERI) {
704 LOG(log_warning, logtype_ad, "Convert OS X to Netatalk AppleDouble: %s",
705 path ? fullpathname(path) : "");
707 if (retry_read > 0) {
708 LOG(log_error, logtype_ad, "ad_header_read_osx: %s, giving up", path ? fullpathname(path) : "");
713 if (ad_convert_osx(path, &adosx) == 1) {
720 if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
721 || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
722 || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
724 LOG(log_error, logtype_ad, "ad_header_read_osx: problem with rfork entry offset.");
731 EC_NEG1( fstat(ad_reso_fileno(ad), &st) );
734 ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(&adosx, ADEID_RFORK));
735 ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
741 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst _U_)
747 char *buf = ad->ad_data;
749 if (ad_meta_fileno(ad) != -1)
750 header_len = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA);
752 header_len = sys_getxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
753 if (header_len < 1) {
754 LOG(log_debug, logtype_ad, "ad_header_read_ea: %s", strerror(errno));
758 if (header_len < AD_DATASZ_EA) {
759 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): short metadata EA", fullpathname(path));
764 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
765 memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
767 ad->ad_magic = ntohl( ad->ad_magic );
768 ad->ad_version = ntohl( ad->ad_version );
770 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
771 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): wrong magic or version", fullpathname(path));
776 memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
777 nentries = ntohs( nentries );
778 if (nentries != ADEID_NUM_EA) {
779 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid number of entries: %d", fullpathname(path), nentries);
784 /* Now parse entries */
785 if (parse_entries(ad, buf + AD_HEADER_LEN, nentries)) {
786 LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
787 path ? fullpathname(path) : "");
792 if (nentries != ADEID_NUM_EA
793 || !ad_entry(ad, ADEID_FINDERI)
794 || !ad_entry(ad, ADEID_COMMENT)
795 || !ad_entry(ad, ADEID_FILEDATESI)
796 || !ad_entry(ad, ADEID_AFPFILEI)
797 || !ad_entry(ad, ADEID_PRIVDEV)
798 || !ad_entry(ad, ADEID_PRIVINO)
799 || !ad_entry(ad, ADEID_PRIVSYN)
800 || !ad_entry(ad, ADEID_PRIVID)) {
801 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid metadata EA", fullpathname(path));
807 * Ensure the resource fork offset is always set
810 if (ad->ad_vers == AD_VERSION_EA)
811 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_OSX);
815 if (ret != 0 && errno == EINVAL) {
817 (void)sys_removexattr(path, AD_EA_META);
819 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): deleted invalid metadata EA", fullpathname(path), nentries);
826 * Takes a path to an AppleDouble file and creates the parrent .AppleDouble directory
829 * path: "/path/.AppleDouble/file"
830 * => mkdir("/path/.AppleDouble/") (in ad_mkdir())
832 static int ad_mkrf(const char *path)
836 * Probably .AppleDouble doesn't exist, try to mkdir it.
838 if (NULL == ( slash = strrchr( path, '/' )) ) {
843 if ( ad_mkdir( path, 0777 ) < 0 ) {
851 static int ad_mkrf_ea(const char *path _U_)
853 AFP_PANIC("ad_mkrf_ea: dont use");
858 static int ad_mkrf_osx(const char *path _U_)
864 if we are root change path user/ group
865 It can be a native function for BSD cf. FAQ.Q10
866 path: pathname to chown
867 stbuf: parent directory inode
869 use fstat and fchown or lchown with linux?
871 #define EMULATE_SUIDDIR
873 static int ad_chown(const char *path, struct stat *stbuf)
876 #ifdef EMULATE_SUIDDIR
879 if (default_uid != (uid_t)-1) {
880 /* we are root (admin) */
881 id = (default_uid)?default_uid:stbuf->st_uid;
882 ret = lchown( path, id, stbuf->st_gid );
888 #define DEFMASK 07700 /* be conservative */
891 return access right and inode of path parent directory
893 static int ad_mode_st(const char *path, mode_t *mode, struct stat *stbuf)
898 if (ad_stat(path, stbuf) != 0) {
902 *mode &= stbuf->st_mode;
906 /* --------------------------- */
907 static int ad_header_upgrade(struct adouble *ad _U_, const char *name _U_)
912 static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
914 AFP_PANIC("ad_header_upgrade_ea: dont use");
919 * Error handling for adouble header(=metadata) file open error
921 * We're called because opening ADFLAGS_HF caused an error.
922 * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
923 * 2. Open non-existent ressource fork, this will just result in first read return EOF
924 * 3. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
925 * ought to close it before returning with an error condition.
927 static int ad_error(struct adouble *ad, int adflags)
930 if (adflags & ADFLAGS_NOHF) { /* 1 */
933 if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_RF) && (errno == ENOENT)) /* 2 */
935 if (adflags & (ADFLAGS_DF | ADFLAGS_SETSHRMD | ADFLAGS_CHECK_OF)) { /* 3 */
936 ad_close( ad, ADFLAGS_DF );
943 * Map ADFLAGS to open() flags
945 * @param adfile (r) the file you really want to open: ADFLAGS_DF or ADFLAGS_HF
946 * @param adflags (r) flags from ad_open(..., adflags, ...)
947 * @returns mapped flags suitable for calling open()
949 static int ad2openflags(const struct adouble *ad, int adfile, int adflags)
953 if (adflags & ADFLAGS_RDWR)
955 if (adflags & ADFLAGS_RDONLY) {
956 if (((adfile == ADFLAGS_DF || adfile == ADFLAGS_RF) && (adflags & ADFLAGS_SETSHRMD))
957 /* need rw access for locks */
958 || ((adfile == ADFLAGS_HF) && (ad->ad_vers == AD_VERSION2)))
959 /* need rw access for adouble file for the case:
960 1) openfork(data+meta:O_RDONLY), 2) openfork(reso(=meta):O_RDWR) */
965 if (adflags & ADFLAGS_CREATE)
967 if (adflags & ADFLAGS_EXCL)
969 if (adflags & ADFLAGS_TRUNC)
972 if (!(ad->ad_options & ADVOL_FOLLO_SYML))
973 oflags |= O_NOFOLLOW;
978 static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble *ad)
987 LOG(log_debug, logtype_ad,
988 "ad_open_df(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
989 fullpathname(path), adflags2logstr(adflags),
990 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
991 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
992 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
994 if (ad_data_fileno(ad) != -1) {
995 /* the file is already open, but we want write access: */
996 if ((adflags & ADFLAGS_RDWR)
997 /* and it was denied the first time: */
998 && (ad->ad_data_fork.adf_flags & O_RDONLY)) {
1002 /* it's not new anymore */
1003 ad->ad_data_fork.adf_flags &= ~( O_TRUNC | O_CREAT );
1004 ad->ad_data_fork.adf_refcount++;
1008 oflags = ad2openflags(ad, ADFLAGS_DF, adflags);
1011 if ((adflags & ADFLAGS_CREATE)) {
1012 st_invalid = ad_mode_st(path, &admode, &st_dir);
1013 if ((ad->ad_options & ADVOL_UNIXPRIV))
1017 ad->ad_data_fork.adf_fd = open(path, oflags, admode);
1019 if (ad->ad_data_fork.adf_fd == -1) {
1024 if ((adflags & ADFLAGS_SETSHRMD) && (adflags & ADFLAGS_RDONLY)) {
1027 EC_NEG1( ad->ad_data_fork.adf_fd = open(path, oflags, admode) );
1031 case OPEN_NOFOLLOW_ERRNO:
1032 ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
1033 if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
1034 free(ad->ad_data_fork.adf_syml);
1037 ad->ad_data_fork.adf_syml[lsz] = 0;
1038 ad->ad_data_fork.adf_fd = AD_SYMLINK;
1046 ad_chown(path, &st_dir); /* just created, set owner if admin (root) */
1048 ad->ad_data_fork.adf_flags = oflags;
1049 adf_lock_init(&ad->ad_data_fork);
1050 ad->ad_data_fork.adf_refcount++;
1053 LOG(log_debug, logtype_ad,
1054 "ad_open_df(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1055 fullpathname(path), adflags2logstr(adflags), ret,
1056 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1057 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1058 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1062 static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adouble *ad)
1066 struct stat st_meta;
1067 struct stat *pst = NULL;
1069 int oflags, nocreatflags, opened = 0;
1071 int st_invalid = -1;
1073 LOG(log_debug, logtype_ad,
1074 "ad_open_hf_v2(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1075 fullpathname(path), adflags2logstr(adflags),
1076 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1077 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1078 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1080 if (ad_meta_fileno(ad) != -1) {
1081 /* the file is already open, but we want write access: */
1082 if ((adflags & ADFLAGS_RDWR) &&
1083 /* and it was already denied: */
1084 (ad->ad_mdp->adf_flags & O_RDONLY)) {
1088 ad_refresh(path, ad);
1089 /* it's not new anymore */
1090 ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
1091 ad->ad_mdp->adf_refcount++;
1095 ad_p = ad->ad_ops->ad_path(path, adflags);
1096 oflags = ad2openflags(ad, ADFLAGS_HF, adflags);
1097 LOG(log_debug, logtype_ad,"ad_open_hf_v2(\"%s\"): open flags: %s",
1098 fullpathname(path), openflags2logstr(oflags));
1099 nocreatflags = oflags & ~(O_CREAT | O_EXCL);
1101 ad_meta_fileno(ad) = open(ad_p, nocreatflags);
1103 if (ad_meta_fileno(ad) != -1) {
1104 ad->ad_mdp->adf_flags = nocreatflags;
1110 if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) {
1111 nocreatflags &= ~O_RDWR;
1112 nocreatflags |= O_RDONLY;
1113 EC_NEG1( ad_meta_fileno(ad) = open(ad_p, nocreatflags) );
1114 ad->ad_mdp->adf_flags = nocreatflags;
1119 if (!(oflags & O_CREAT))
1122 * We're expecting to create a new adouble header file here
1124 LOG(log_debug, logtype_ad, "ad_open(\"%s\"): creating adouble file",
1125 fullpathname(path));
1128 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1129 if ((ad->ad_options & ADVOL_UNIXPRIV))
1131 admode = ad_hf_mode(admode);
1132 if (errno == ENOENT) {
1133 EC_NEG1_LOG( ad->ad_ops->ad_mkrf(ad_p) );
1135 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1136 if ((ad->ad_options & ADVOL_UNIXPRIV))
1138 admode = ad_hf_mode(admode);
1141 /* retry with O_CREAT */
1142 EC_NEG1( ad_meta_fileno(ad) = open(ad_p, oflags, admode) );
1143 ad->ad_mdp->adf_flags = oflags;
1144 /* just created, set owner if admin owner (root) */
1146 ad_chown(ad_p, &st_dir);
1153 /* Now we've got a new opened fd, we need to check that in the error case */
1156 if (!(ad->ad_mdp->adf_flags & O_CREAT)) {
1157 /* check for 0 length files, treat them as new. */
1158 if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0) {
1159 if (st_meta.st_size == 0)
1160 ad->ad_mdp->adf_flags |= O_TRUNC;
1162 /* we have valid data in st_meta stat structure, reused it in ad_header_read */
1167 adf_lock_init(ad->ad_mdp);
1168 ad->ad_mdp->adf_refcount = 1;
1170 if ((ad->ad_mdp->adf_flags & ( O_TRUNC | O_CREAT ))) {
1171 /* This is a new adouble header file, create it */
1172 EC_NEG1_LOG( new_ad_header(ad, path, pst, adflags) );
1175 /* Read the adouble header in and parse it.*/
1176 EC_NEG1_LOG( ad->ad_ops->ad_header_read(path, ad, pst) );
1180 if (ret != 0 && opened && ad_meta_fileno(ad) != -1) {
1181 close(ad_meta_fileno(ad));
1182 ad_meta_fileno(ad) = -1;
1183 ad->ad_mdp->adf_refcount = 0;
1185 LOG(log_debug, logtype_ad,
1186 "ad_open_hf_v2(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1187 fullpathname(path), adflags2logstr(adflags), ret,
1188 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1189 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1190 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1194 static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
1200 LOG(log_debug, logtype_ad,
1201 "ad_open_hf_ea(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1202 fullpathname(path), adflags2logstr(adflags),
1203 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1204 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1205 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1207 oflags = ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC);
1209 if (ad_meta_fileno(ad) == AD_SYMLINK)
1212 if (ad_meta_fileno(ad) != -1) {
1213 /* the file is already open, but we want write access: */
1214 if ((oflags & O_RDWR) &&
1215 /* and it was already denied: */
1216 (ad->ad_mdp->adf_flags & O_RDONLY)) {
1217 LOG(log_error, logtype_ad, "ad_open_hf_ea(%s): rw request for ro file: %s",
1218 fullpathname(path), strerror(errno));
1222 /* it's not new anymore */
1223 ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
1225 if (oflags & O_RDWR) {
1226 /* Fo a RDONLY adouble we just use sys_lgetxattr instead if sys_fgetxattr */
1227 if (adflags & ADFLAGS_DIR)
1228 /* For directories we open the directory RDONYL so we can later fchdir() */
1229 oflags = (oflags & ~O_RDWR) | O_RDONLY;
1230 LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
1231 EC_NEG1(ad_meta_fileno(ad) = open(path, oflags));
1233 ad->ad_mdp->adf_flags = oflags;
1237 /* Read the adouble header in and parse it.*/
1238 if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) {
1239 if (!(adflags & ADFLAGS_CREATE)) {
1240 LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): can't read metadata EA", path);
1244 if ((adflags & ADFLAGS_CREATE) && (ad->ad_options & ADVOL_RO)) {
1249 LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
1251 /* It doesnt exist, EPERM or another error */
1252 if (!(errno == ENOATTR || errno == ENOENT)) {
1253 LOG(log_error, logtype_ad, "ad_open_hf_ea: unexpected: %s", strerror(errno));
1258 EC_NEG1_LOG(new_ad_header(ad, path, NULL, adflags));
1259 ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
1261 LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
1264 if (ad_meta_fileno(ad) != -1)
1265 ad->ad_mdp->adf_refcount++;
1266 ad->ad_rlen = ad_reso_size(path, adflags, ad);
1269 if (ret != 0 && opened && ad_meta_fileno(ad) != -1) {
1270 close(ad_meta_fileno(ad));
1271 ad_meta_fileno(ad) = -1;
1272 ad->ad_mdp->adf_refcount = 0;
1274 LOG(log_debug, logtype_ad,
1275 "ad_open_hf_ea(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1276 fullpathname(path), adflags2logstr(adflags), ret,
1277 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1278 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1279 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1284 static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *ad)
1288 ad->ad_meta_refcount++;
1290 switch (ad->ad_vers) {
1292 ret = ad_open_hf_v2(path, adflags, mode, ad);
1295 ret = ad_open_hf_ea(path, adflags, mode, ad);
1303 ad->ad_meta_refcount--;
1304 ret = ad_error(ad, adflags);
1311 * Get resofork length for adouble:ea, parameter 'ad' may be NULL
1313 off_t ad_reso_size(const char *path, int adflags, struct adouble *ad)
1319 if (adflags & ADFLAGS_DIR)
1322 LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): BEGIN", path);
1327 if (ad && ad_reso_fileno(ad) != -1) {
1328 EC_NEG1( fstat(ad_reso_fileno(ad), &st) );
1330 } else if (ad && ad_meta_fileno(ad) != -1) {
1331 EC_NEG1( (rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) );
1333 EC_NEG1( (rlen = sys_lgetxattr(path, AD_EA_RESO, NULL, 0)) );
1338 EC_NULL_LOG( rfpath = ad_path_osx(path, adflags));
1339 EC_ZERO( lstat(rfpath, &st));
1340 if (st.st_size > ADEDOFF_RFORK_OSX)
1341 rlen = st.st_size - ADEDOFF_RFORK_OSX;
1346 LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): size: %zd", path, rlen);
1354 static int ad_open_rf_v2(const char *path, int adflags, int mode, struct adouble *ad)
1359 * ad_open_hf_v2() does the work, but if it failed and adflags are ADFLAGS_NOHF | ADFLAGS_RF
1360 * ad_open_hf_v2() didn't give an error, but we're supposed to return a reso fork fd
1363 LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): BEGIN", fullpathname(path));
1365 if (!AD_META_OPEN(ad) && !(adflags & (ADFLAGS_NORF | ADFLAGS_RDONLY)))
1367 if (AD_META_OPEN(ad))
1368 ad->ad_reso_refcount++;
1371 LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): END: %d", fullpathname(path), ret);
1375 static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble *ad)
1380 int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF);
1386 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
1388 oflags = ad2openflags(ad, ADFLAGS_RF, adflags) & ~O_CREAT;
1390 if (ad_reso_fileno(ad) != -1) {
1391 /* the file is already open, but we want write access: */
1392 if ((oflags & O_RDWR)
1393 /* and it was already denied: */
1394 && (ad->ad_rfp->adf_flags & O_RDONLY)) {
1398 ad->ad_rfp->adf_flags &= ~( O_TRUNC | O_CREAT );
1399 ad->ad_reso_refcount++;
1400 ad->ad_rfp->adf_refcount++;
1401 EC_NEG1_LOG( ad->ad_rlen = ad_reso_size(path, adflags, ad));
1405 if (ad_meta_fileno(ad) < 0)
1407 if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
1408 if (!(adflags & ADFLAGS_CREATE)) {
1413 if (!(adflags & ADFLAGS_RDONLY)) {
1414 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
1419 if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
1420 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
1427 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
1432 EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad),
1433 AD_EA_RESO, oflags, 0666) );
1437 EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags) );
1438 if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
1439 if (!(adflags & ADFLAGS_CREATE)) {
1444 if (!(adflags & ADFLAGS_RDONLY)) {
1445 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
1450 if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
1451 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
1458 LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
1463 EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
1464 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
1470 ad->ad_rfp->adf_refcount = 1;
1471 ad->ad_rfp->adf_flags = oflags;
1472 ad->ad_reso_refcount++;
1475 EC_ZERO_LOG( fstat(ad_reso_fileno(ad), &st) );
1476 if (ad->ad_rfp->adf_flags & O_CREAT) {
1477 /* This is a new adouble header file, create it */
1478 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
1480 EC_NEG1_LOG( new_ad_header(ad, path, NULL, adflags) );
1481 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
1485 /* Read the adouble header */
1486 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
1488 EC_NEG1_LOG( ad_header_read_osx(rfpath, ad, &st) );
1492 ad->ad_rlen = ad_reso_size(path, adflags, ad);
1496 if (opened && (ad_reso_fileno(ad) != -1)) {
1497 close(ad_reso_fileno(ad));
1498 ad_reso_fileno(ad) = -1;
1499 ad->ad_reso_refcount--;
1500 ad->ad_rfp->adf_refcount = 0;
1502 if (adflags & ADFLAGS_NORF) {
1506 (void)ad_close(ad, closeflags);
1512 LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
1518 * Open ressource fork
1520 static int ad_open_rf(const char *path, int adflags, int mode, struct adouble *ad)
1524 switch (ad->ad_vers) {
1526 ret = ad_open_rf_v2(path, adflags, mode, ad);
1529 ret = ad_open_rf_ea(path, adflags, mode, ad);
1539 /***********************************************************************************
1541 ********************************************************************************* */
1543 off_t ad_getentryoff(const struct adouble *ad, int eid)
1545 if (ad->ad_vers == AD_VERSION2)
1546 return ad->ad_eid[eid].ade_off;
1555 return ad->ad_eid[eid].ade_off;
1558 return ad->ad_eid[eid].ade_off;
1561 AFP_PANIC("What am I doing here?");
1564 const char *ad_path_ea( const char *path, int adflags _U_)
1569 const char *ad_path_osx(const char *path, int adflags _U_)
1571 static char pathbuf[ MAXPATHLEN + 1];
1572 char c, *slash, buf[MAXPATHLEN + 1];
1574 if (!strcmp(path,".")) {
1576 getcwd(buf, MAXPATHLEN);
1579 strlcpy(buf, path, MAXPATHLEN +1);
1581 if (NULL != ( slash = strrchr( buf, '/' )) ) {
1584 strlcpy( pathbuf, buf, MAXPATHLEN +1);
1587 pathbuf[ 0 ] = '\0';
1590 strlcat( pathbuf, "._", MAXPATHLEN +1);
1591 strlcat( pathbuf, slash, MAXPATHLEN +1);
1596 * Put the .AppleDouble where it needs to be:
1598 * / a/.AppleDouble/b
1600 * \ b/.AppleDouble/.Parent
1602 * FIXME: should do something for pathname > MAXPATHLEN
1604 const char *ad_path( const char *path, int adflags)
1606 static char pathbuf[ MAXPATHLEN + 1];
1610 if ( adflags & ADFLAGS_DIR ) {
1611 l = strlcpy( pathbuf, path, sizeof(pathbuf));
1613 if ( l && l < MAXPATHLEN) {
1616 strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
1618 if (NULL != ( slash = strrchr( path, '/' )) ) {
1621 /* XXX we must return NULL here and test in the caller */
1624 memcpy( pathbuf, path, l);
1629 l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
1630 strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
1636 /* -------------------------
1637 * Support inherited protection modes for AppleDouble files. The supplied
1638 * mode is ANDed with the parent directory's mask value in lieu of "umask",
1639 * and that value is returned.
1641 char *ad_dir(const char *path)
1643 static char modebuf[ MAXPATHLEN + 1];
1646 * For a path with directories in it, remove the final component
1647 * (path or subdirectory name) to get the name we want to stat.
1648 * For a path which is just a filename, use "." instead.
1650 slash = strrchr( path, '/' );
1655 if (len >= MAXPATHLEN) {
1656 errno = ENAMETOOLONG;
1657 return NULL; /* can't do it */
1659 memcpy( modebuf, path, len );
1660 modebuf[len] = '\0';
1661 /* is last char a '/' ? */
1662 if (slash[1] == 0) {
1663 slash = modebuf+ len;
1665 while (modebuf < slash && slash[-1] == '/') {
1668 if (modebuf == slash) {
1672 while (modebuf < slash && *slash != '/') {
1675 if (modebuf == slash) {
1678 *slash = '\0'; /* remove pathname component */
1683 modebuf[0] = '.'; /* use current directory */
1688 int ad_setfuid(const uid_t id)
1694 /* ---------------- */
1695 uid_t ad_getfuid(void)
1701 stat path parent directory
1703 int ad_stat(const char *path, struct stat *stbuf)
1710 return stat( p, stbuf );
1714 return access right of path parent directory
1716 int ad_mode( const char *path, mode_t mode)
1719 ad_mode_st(path, &mode, &stbuf);
1724 * Use mkdir() with mode bits taken from ad_mode().
1726 int ad_mkdir( const char *path, mode_t mode)
1732 LOG(log_debug, logtype_ad, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
1733 path, mode, getcwdpath());
1735 st_invalid = ad_mode_st(path, &mode, &stbuf);
1736 ret = mkdir( path, mode );
1737 if (ret || st_invalid)
1739 ad_chown(path, &stbuf);
1744 static void ad_init_func(struct adouble *ad)
1746 switch (ad->ad_vers) {
1748 ad->ad_ops = &ad_adouble;
1749 ad->ad_rfp = &ad->ad_resource_fork;
1750 ad->ad_mdp = &ad->ad_resource_fork;
1753 ad->ad_ops = &ad_adouble_ea;
1754 ad->ad_rfp = &ad->ad_resource_fork;
1755 ad->ad_mdp = &ad->ad_data_fork;
1758 AFP_PANIC("ad_init: unknown AD version");
1762 ad_data_fileno(ad) = -1;
1763 ad_reso_fileno(ad) = -1;
1764 ad_meta_fileno(ad) = -1;
1765 ad->ad_refcount = 1;
1770 void ad_init_old(struct adouble *ad, int flags, int options)
1772 memset(ad, 0, sizeof(struct adouble));
1773 ad->ad_vers = flags;
1774 ad->ad_options = options;
1778 void ad_init(struct adouble *ad, const struct vol * restrict vol)
1780 memset(ad, 0, sizeof(struct adouble));
1781 ad->ad_vers = vol->v_adouble;
1782 ad->ad_options = vol->v_ad_options;
1787 * Open data-, metadata(header)- or ressource fork
1789 * ad_open(struct adouble *ad, const char *path, int adflags, int flags)
1790 * ad_open(struct adouble *ad, const char *path, int adflags, int flags, mode_t mode)
1792 * You must call ad_init() before ad_open, usually you'll just call it like this: \n
1795 * ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1798 * Open a files data fork, metadata fork or ressource fork.
1800 * @param ad (rw) pointer to struct adouble
1801 * @param path (r) Path to file or directory
1802 * @param adflags (r) Flags specifying which fork to open, can be or'd:
1803 * ADFLAGS_DF: open data fork
1804 * ADFLAGS_RF: open ressource fork
1805 * ADFLAGS_HF: open header (metadata) file
1806 * ADFLAGS_NOHF: it's not an error if header file couldn't be opened
1807 * ADFLAGS_NORF: it's not an error if reso fork couldn't be opened
1808 * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags
1810 * Access mode for the forks:
1811 * ADFLAGS_RDONLY: open read only
1812 * ADFLAGS_RDWR: open read write
1815 * ADFLAGS_CREATE: create if not existing
1816 * ADFLAGS_TRUNC: truncate
1819 * ADFLAGS_CHECK_OF: check for open forks from us and other afpd's
1820 * ADFLAGS_SETSHRMD: this adouble struct will be used to set sharemode locks.
1821 * This basically results in the files being opened RW instead of RDONLY.
1822 * @param mode (r) mode used with O_CREATE
1824 * The open mode flags (rw vs ro) have to take into account all the following requirements:
1825 * - we remember open fds for files because me must avoid a single close releasing fcntl locks for other
1826 * fds of the same file
1830 * * on Solaris (HAVE_EAFD) ADFLAGS_RF doesn't work without
1831 * ADFLAGS_HF, because it checks whether ad_meta_fileno() is already
1832 * openend. As a workaround pass ADFLAGS_SETSHRMD.
1834 * @returns 0 on success, any other value indicates an error
1836 int ad_open(struct adouble *ad, const char *path, int adflags, ...)
1842 LOG(log_debug, logtype_ad,
1843 "ad_open(\"%s\", %s): BEGIN {d: %d, m: %d, r: %d}"
1844 "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1845 fullpathname(path), adflags2logstr(adflags),
1846 ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
1847 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1848 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1849 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1851 if (adflags & ADFLAGS_CHECK_OF)
1852 /* Checking for open forks requires sharemode lock support (ie RDWR instead of RDONLY) */
1853 adflags |= ADFLAGS_SETSHRMD;
1855 if (adflags & ADFLAGS_SETSHRMD)
1856 /* sharemode locks are stored in the data fork */
1857 adflags |= ADFLAGS_DF;
1859 if (ad->ad_vers == AD_VERSION2) {
1860 if (adflags & ADFLAGS_RF)
1861 adflags |= ADFLAGS_HF;
1862 if (adflags & ADFLAGS_NORF)
1863 adflags |= ADFLAGS_NOHF;
1866 if (ad->ad_inited != AD_INITED) {
1867 ad->ad_adflags = adflags;
1868 ad->ad_inited = AD_INITED;
1870 ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1871 if (ad->ad_resource_fork.adf_refcount > 0)
1872 ad->ad_open_forks |= ATTRBIT_ROPEN;
1875 va_start(args, adflags);
1876 if (adflags & ADFLAGS_CREATE)
1877 mode = (sizeof(mode_t) < sizeof(int) ? va_arg (args, int) : va_arg (args, mode_t));
1880 if (adflags & ADFLAGS_DF) {
1881 ad->ad_data_refcount++;
1882 if (ad_open_df(path, adflags, mode, ad) != 0) {
1883 ad->ad_data_refcount--;
1888 if (adflags & ADFLAGS_HF) {
1889 if (ad_open_hf(path, adflags, mode, ad) != 0) {
1894 if (adflags & ADFLAGS_RF) {
1895 if (ad_open_rf(path, adflags, mode, ad) != 0) {
1900 if (adflags & ADFLAGS_CHECK_OF) {
1901 ad->ad_open_forks |= ad_openforks(ad, ad->ad_open_forks);
1905 LOG(log_debug, logtype_ad,
1906 "ad_open(\"%s\"): END: %d {d: %d, m: %d, r: %d}"
1907 "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
1908 fullpathname(path), ret,
1909 ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
1910 ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
1911 ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
1912 ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
1918 * @brief open metadata, possibly as root
1920 * Return only metadata but try very hard ie at first try as user, then try as root.
1922 * @param name name of file/dir
1923 * @param flags ADFLAGS_DIR: name is a directory \n
1924 * ADFLAGS_CHECK_OF: test if name is open by us or another afpd process
1926 * @param adp pointer to struct adouble
1928 int ad_metadata(const char *name, int flags, struct adouble *adp)
1930 int ret, err, oflags;
1932 /* Sanitize flags */
1933 oflags = (flags & (ADFLAGS_CHECK_OF | ADFLAGS_DIR)) | ADFLAGS_HF | ADFLAGS_RDONLY;
1935 if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) {
1937 ret = ad_open(adp, name, oflags);
1947 * @brief openat like wrapper for ad_metadata
1949 int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
1955 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
1961 if (ad_metadata(name, flags, adp) < 0) {
1968 if (fchdir(cwdfd) != 0) {
1969 LOG(log_error, logtype_ad, "ad_openat: cant chdir back, exiting");
1982 int ad_refresh(const char *path, struct adouble *ad)
1984 switch (ad->ad_vers) {
1986 if (ad_meta_fileno(ad) == -1)
1988 return ad->ad_ops->ad_header_read(NULL, ad, NULL);
1992 if (AD_META_OPEN(ad)) {
1993 if (ad_data_fileno(ad) == -1)
1995 // TODO: read meta EA
1998 if (AD_RSRC_OPEN(ad)) {
1999 if (ad_reso_fileno(ad) == -1)
2002 if (fstat(ad_reso_fileno(ad), &st) == -1)
2004 ad->ad_rlen = st.st_size;
2007 if (AD_META_OPEN(ad)) {
2008 if (ad_data_fileno(ad) == -1)
2010 // TODO: read meta EA
2012 if (AD_RSRC_OPEN(ad)) {
2013 if (ad_reso_fileno(ad) == -1)
2015 if (ad_header_read_osx(path, ad, NULL) < 0)
2019 return ad->ad_ops->ad_header_read(path, ad, NULL);
2028 int ad_openat(struct adouble *ad,
2029 int dirfd, /* dir fd openat like */
2039 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0))
2043 va_start(args, adflags);
2044 if (adflags & ADFLAGS_CREATE)
2045 mode = (sizeof(mode_t) < sizeof(int) ? va_arg (args, int) : va_arg (args, mode_t));
2048 EC_NEG1( ad_open(ad, path, adflags, mode) );
2051 if (fchdir(cwdfd) != 0) {
2052 AFP_PANIC("ad_openat: cant chdir back");
2063 /* build a resource fork mode from the data fork mode:
2064 * remove X mode and extend header to RW if R or W (W if R for locking),
2066 mode_t ad_hf_mode(mode_t mode)
2068 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
2069 /* fnctl lock need write access */
2070 if ((mode & S_IRUSR))
2072 if ((mode & S_IRGRP))
2074 if ((mode & S_IROTH))
2077 /* if write mode set add read mode */
2078 if ((mode & S_IWUSR))
2080 if ((mode & S_IWGRP))
2082 if ((mode & S_IWOTH))