From 172700ab3cd0ec6839239a2b677b2aacd0e6ca1f Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Thu, 19 Jan 2012 17:32:46 +0100 Subject: [PATCH] First working adouble:ea->osx fallback working --- etc/afpd/fork.c | 6 +- etc/afpd/ofork.c | 2 +- include/atalk/adouble.h | 41 ++- include/atalk/ea.h | 1 + libatalk/adouble/ad_flush.c | 236 +++++++++-------- libatalk/adouble/ad_open.c | 515 +++++++++++++++++++++++++----------- libatalk/adouble/ad_read.c | 38 +-- libatalk/adouble/ad_write.c | 53 +--- libatalk/vfs/extattr.c | 25 ++ 9 files changed, 576 insertions(+), 341 deletions(-) diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index 6eff75a8..df6a2d8a 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -561,7 +561,7 @@ int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U if (err < 0) goto afp_setfork_err; } else if (bitmap == (1<of_ad ); + ad_refresh(NULL, ofork->of_ad ); st_size = ad_size(ofork->of_ad, eid); err = -2; @@ -1067,7 +1067,7 @@ int flushfork(struct ofork *ofork) (ofork->of_flags & AFPFORK_RSRC)) { /* read in the rfork length */ - ad_refresh(ofork->of_ad); + ad_refresh(NULL, ofork->of_ad); /* set the date if we're dirty */ if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) { @@ -1358,7 +1358,7 @@ int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbu } if (AD_META_OPEN(ofork->of_ad)) { - if ( ad_refresh( ofork->of_ad ) < 0 ) { + if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) { LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) ); return( AFPERR_PARAM ); } diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index 326acd0b..362097ae 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -404,7 +404,7 @@ int of_closefork(struct ofork *ofork) if (ofork->of_flags & AFPFORK_RSRC) { adflags |= ADFLAGS_RF; /* Only set the rfork's length if we're closing the rfork. */ - ad_refresh( ofork->of_ad ); + ad_refresh(NULL, ofork->of_ad ); if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) { ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec); ad_flush( ofork->of_ad ); diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index 51330885..65946028 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -49,6 +49,7 @@ /* version info */ #define AD_VERSION2 0x00020000 +#define AD_VERSION2_OSX 0x00020001 #define AD_VERSION_EA 0x00020002 /* default */ @@ -96,7 +97,7 @@ #define ADEDLEN_VERSION 4 #define ADEDLEN_FILLER 16 #define ADEDLEN_NENTRIES 2 -#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + ADEDLEN_FILLER + ADEDLEN_NENTRIES) +#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */ #define AD_ENTRY_LEN 12 /* size of a single entry header */ /* field widths */ @@ -117,7 +118,8 @@ #define ADEDLEN_PRIVID 4 #define ADEID_NUM_V2 13 -#define ADEID_NUM_EA 5 +#define ADEID_NUM_EA 6 +#define ADEID_NUM_OSX 2 #define AD_DATASZ2 (AD_HEADER_LEN + ADEDLEN_NAME + ADEDLEN_COMMENT + ADEDLEN_FILEI + \ ADEDLEN_FINDERI + ADEDLEN_DID + ADEDLEN_AFPFILEI + ADEDLEN_SHORTNAME + \ @@ -130,10 +132,16 @@ #define AD_DATASZ_EA (AD_HEADER_LEN + (ADEID_NUM_EA * AD_ENTRY_LEN) + ADEDLEN_FINDERI + \ ADEDLEN_COMMENT + ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + ADEDLEN_PRIVID) -#if AD_DATASZ_EA != 342 +#if AD_DATASZ_EA != 354 #error bad size for AD_DATASZ_EA #endif +#define AD_DATASZ_OSX (AD_HEADER_LEN + (ADEID_NUM_OSX * AD_ENTRY_LEN) + ADEDLEN_FINDERI) + +#if AD_DATASZ_OSX != 82 +#error bad size for AD_DATASZ_OSX +#endif + #define AD_DATASZ_MAX 1024 #if AD_VERSION == AD_VERSION2 @@ -142,7 +150,9 @@ #define AD_DATASZ AD_DATASZ_EA #endif -#define RFORK_EA_ALLOCSIZE (128*1024) /* 128k */ +/* fallback for ad:ea on filesystems without fds for EAs, like old adouble:osx */ +#define ADEDOFF_FINDERI_OSX (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN) +#define ADEDOFF_RFORK_OSX (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI) typedef uint32_t cnid_t; @@ -175,7 +185,7 @@ struct adouble_fops { const char *(*ad_path)(const char *, int); int (*ad_mkrf)(const char *); int (*ad_rebuild_header)(struct adouble *); - int (*ad_header_read)(struct adouble *, struct stat *); + int (*ad_header_read)(const char *, struct adouble *, struct stat *); int (*ad_header_upgrade)(struct adouble *, const char *); }; @@ -184,21 +194,26 @@ struct adouble { uint32_t ad_version; /* Official adouble version number */ char ad_filler[16]; struct ad_entry ad_eid[ADEID_MAX]; + struct ad_fd ad_data_fork; /* the data fork */ + + struct ad_fd ad_metadata_fork; /* adouble:v2 -> unused * + * adouble:ea -> fd unused, only flags used */ + struct ad_fd ad_resource_fork; /* adouble:v2 -> the adouble file * - * adouble:ea -> unused */ + * adouble:ea -> the EA fd */ + struct ad_fd *ad_rfp; /* adouble:v2 -> ad_resource_fork * - * adouble:ea -> ad_data_fork */ + * adouble:ea -> ad_resource_fork */ + struct ad_fd *ad_mdp; /* adouble:v2 -> ad_resource_fork * - * adouble:ea -> ad_data_fork */ + * adouble:ea -> ad_metadata_fork */ + int ad_vers; /* Our adouble version info (AD_VERSION*) */ int ad_adflags; /* ad_open flags adflags like ADFLAGS_DIR */ uint32_t ad_inited; int ad_options; int ad_refcount; /* multiple forks may open one adouble */ - void *ad_resforkbuf; /* buffer for AD_VERSION_EA ressource fork */ - size_t ad_resforkbufsize; /* size of ad_resforkbuf */ - size_t ad_maxeafssize; /* maximum EA size allowed from the fs */ off_t ad_rlen; /* ressource fork len with AFP 3.0 * * the header parameter size is too small. */ char *ad_m_name; /* mac name for open fork */ @@ -344,6 +359,7 @@ struct adouble { #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len) #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len)) #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off) +#define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off)) #define ad_entry(ad,eid) ((caddr_t)(ad)->ad_data + (ad)->ad_eid[(eid)].ade_off) #define ad_get_RF_flags(ad) ((ad)->ad_rfp->adf_flags) @@ -355,7 +371,6 @@ struct adouble { /* ad_flush.c */ extern int ad_rebuild_adouble_header (struct adouble *); -extern int ad_rebuild_sfm_header (struct adouble *); extern int ad_copy_header (struct adouble *, struct adouble *); extern int ad_flush (struct adouble *); extern int ad_close (struct adouble *, int); @@ -382,7 +397,7 @@ extern void ad_init (struct adouble *, const struct vol * restrict); extern void ad_init_old (struct adouble *ad, int flags, int options); extern int ad_open (struct adouble *ad, const char *path, int adflags, ...); extern int ad_openat (struct adouble *, int dirfd, const char *path, int adflags, ...); -extern int ad_refresh (struct adouble *); +extern int ad_refresh (const char *path, struct adouble *); extern int ad_stat (const char *, struct stat *); extern int ad_metadata (const char *, int, struct adouble *); extern int ad_metadataat (int, const char *, int, struct adouble *); diff --git a/include/atalk/ea.h b/include/atalk/ea.h index f26e32b2..63534cdc 100644 --- a/include/atalk/ea.h +++ b/include/atalk/ea.h @@ -94,6 +94,7 @@ int sys_setxattr (const char *path, const char *name, const void *value, size_t int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags); int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags); int sys_copyxattr (const char *src, const char *dst); +int sys_getxattrfd(const char *path, const char *uname, int oflag, ...); /**************************************************************************************** * Stuff for our implementation of storing EAs in files in .AppleDouble dirs diff --git a/libatalk/adouble/ad_flush.c b/libatalk/adouble/ad_flush.c index 52f24f17..e0321a39 100644 --- a/libatalk/adouble/ad_flush.c +++ b/libatalk/adouble/ad_flush.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "ad_lock.h" @@ -48,50 +49,10 @@ static const uint32_t set_eid[] = { #define EID_DISK(a) (set_eid[a]) -int fsetrsrcea(struct adouble *ad, int fd, const char *eaname, const void *value, size_t size, int flags) -{ - if ((ad->ad_maxeafssize == 0) || (ad->ad_maxeafssize >= size)) { - LOG(log_debug, logtype_default, "fsetrsrcea(\"%s\"): size: %zu", eaname, size); - if (sys_fsetxattr(fd, eaname, value, size, 0) == -1) - return -1; - return 0; - } - - /* rsrcfork is larger then maximum EA support by fs so we have to split it */ - int i; - int eas = (size / ad->ad_maxeafssize); - size_t remain = size - (eas * ad->ad_maxeafssize); - bstring eachunk; - - LOG(log_debug, logtype_default, "fsetrsrcea(\"%s\"): size: %zu, maxea: %zu, eas: %d, remain: %zu", - eaname, size, ad->ad_maxeafssize, eas, remain); - - for (i = 0; i < eas; i++) { - if ((eachunk = bformat("%s.%d", eaname, i + 1)) == NULL) - return -1; - if (sys_fsetxattr(fd, bdata(eachunk), value + (i * ad->ad_maxeafssize), ad->ad_maxeafssize, 0) == -1) { - LOG(log_error, logtype_default, "fsetrsrcea(\"%s\"): %s", bdata(eachunk), strerror(errno)); - bdestroy(eachunk); - return -1; - } - bdestroy(eachunk); - } - - if ((eachunk = bformat("%s.%d", eaname, i + 1)) == NULL) - return -1; - if (sys_fsetxattr(fd, bdata(eachunk), value + (i * ad->ad_maxeafssize), remain, 0) == -1) { - LOG(log_error, logtype_default, "fsetrsrcea(\"%s\"): %s", bdata(eachunk), strerror(errno)); - return -1; - } - bdestroy(eachunk); - - return 0; -} - /* - * Rebuild any header information that might have changed. + * Prepare ad->ad_data buffer from struct adouble for writing on disk */ -int ad_rebuild_adouble_header(struct adouble *ad) +int ad_rebuild_adouble_header(struct adouble *ad) { uint32_t eid; uint32_t temp; @@ -149,6 +110,65 @@ int ad_rebuild_adouble_header(struct adouble *ad) return len; } +/*! + * Prepare adbuf buffer from struct adouble for writing on disk + */ +static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf) +{ + uint32_t temp; + uint16_t nent; + char *buf; + int len; + + LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_osx"); + + buf = &adbuf[0]; + + temp = htonl( ad->ad_magic ); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + temp = htonl( ad->ad_version ); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + buf += sizeof( ad->ad_filler ); + + nent = htons(ADEID_NUM_OSX); + memcpy(buf, &nent, sizeof( nent )); + buf += sizeof( nent ); + + /* FinderInfo */ + temp = htonl(EID_DISK(ADEID_FINDERI)); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + temp = htonl(ADEDOFF_FINDERI_OSX); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + temp = htonl(ADEDLEN_FINDERI); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + memcpy(adbuf + ADEDOFF_FINDERI_OSX, ad_entry(ad, ADEID_FINDERI), ADEDLEN_FINDERI); + + /* rfork */ + temp = htonl( EID_DISK(ADEID_RFORK) ); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + temp = htonl(ADEDOFF_RFORK_OSX); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + temp = htonl( ad->ad_rlen); + memcpy(buf, &temp, sizeof( temp )); + buf += sizeof( temp ); + + return AD_DATASZ_OSX; +} + /* ------------------- * XXX copy only header with same size or comment * doesn't work well for adouble with different version. @@ -184,11 +204,11 @@ int ad_copy_header(struct adouble *add, struct adouble *ads) return 0; } -int ad_flush(struct adouble *ad) +static int ad_flush_hf(struct adouble *ad) { int len; - LOG(log_debug, logtype_default, "ad_flush(%s)", adflags2logstr(ad->ad_adflags)); + LOG(log_debug, logtype_default, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags)); struct ad_fd *adf; @@ -229,12 +249,6 @@ int ad_flush(struct adouble *ad) return -1; } } -#ifndef HAVE_EAFD - if (AD_RSRC_OPEN(ad) && (ad->ad_rlen > 0)) { - if (fsetrsrcea(ad, ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen, 0) == -1) - return -1; - } -#endif break; default: LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version"); @@ -245,6 +259,56 @@ int ad_flush(struct adouble *ad) return( 0 ); } +/* Flush resofork adouble file if any (currently adouble:ea and #ifndef HAVE_EAFD eg Linux) */ +static int ad_flush_rf(struct adouble *ad) +{ + ssize_t len; + char adbuf[AD_DATASZ_OSX]; + +#ifdef HAVE_EAFD + return 0; +#endif + if (ad->ad_vers != AD_VERSION_EA) + return 0; + + LOG(log_debug, logtype_default, "ad_flush_rf(%s)", adflags2logstr(ad->ad_adflags)); + + if ((ad->ad_rfp->adf_flags & O_RDWR)) { + if (ad_getentryoff(ad, ADEID_RFORK)) { + if (ad->ad_rlen > 0xffffffff) + ad_setentrylen(ad, ADEID_RFORK, 0xffffffff); + else + ad_setentrylen(ad, ADEID_RFORK, ad->ad_rlen); + } + len = ad_rebuild_adouble_header_osx(ad, &adbuf[0]); + + if (adf_pwrite(ad->ad_rfp, adbuf, len, 0) != len) { + if (errno == 0) + errno = EIO; + return -1; + } + } + return 0; +} + +int ad_flush(struct adouble *ad) +{ + EC_INIT; + + LOG(log_debug, logtype_default, "ad_flush(%s)", adflags2logstr(ad->ad_adflags)); + + if (AD_META_OPEN(ad)) { + EC_ZERO( ad_flush_hf(ad) ); + } + + if (AD_RSRC_OPEN(ad)) { + EC_ZERO( ad_flush_rf(ad) ); + } + +EC_CLEANUP: + EC_EXIT; +} + static int ad_data_closefd(struct adouble *ad) { int ret = 0; @@ -272,6 +336,10 @@ int ad_close(struct adouble *ad, int adflags) LOG(log_debug, logtype_default, "ad_close(%s)", adflags2logstr(adflags)); + /* cf ad_open(): we opened the datafork too for sharemode locks */ + if ((ad->ad_vers == AD_VERSION_EA) && (ad->ad_adflags & ADFLAGS_SETSHRMD)) + adflags |= ADFLAGS_DF; + if ((adflags & ADFLAGS_DF) && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ && --ad->ad_data_fork.adf_refcount == 0) { @@ -280,62 +348,22 @@ int ad_close(struct adouble *ad, int adflags) adf_lock_free(&ad->ad_data_fork); } - if ((adflags & ADFLAGS_HF)) { - switch (ad->ad_vers) { - case AD_VERSION2: - if ((ad_meta_fileno(ad) != -1) && !(--ad->ad_mdp->adf_refcount)) { - if (close( ad_meta_fileno(ad) ) < 0) - err = -1; - ad_meta_fileno(ad) = -1; - adf_lock_free(ad->ad_mdp); - } - break; - - case AD_VERSION_EA: - if ((ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ - && !(--ad->ad_data_fork.adf_refcount)) { - if (ad_data_closefd(ad) < 0) - err = -1; - adf_lock_free(&ad->ad_data_fork); - } - break; - - default: - LOG(log_error, logtype_default, "ad_close: unknown AD version"); - errno = EIO; - return -1; - } + if ((adflags & ADFLAGS_HF) + && (ad_meta_fileno(ad) != -1) && !(--ad->ad_mdp->adf_refcount)) { + if (close( ad_meta_fileno(ad)) < 0) + err = -1; + ad_meta_fileno(ad) = -1; + if (ad->ad_vers == AD_VERSION2) + adf_lock_free(ad->ad_mdp); } - if ((adflags & ADFLAGS_RF)) { - switch (ad->ad_vers) { - case AD_VERSION2: - /* Do nothing as ADFLAGS_RF == ADFLAGS_HF */ - break; - - case AD_VERSION_EA: -#ifndef HAVE_EAFD - LOG(log_debug, logtype_default, "ad_close: ad->ad_rlen: %zu", ad->ad_rlen); - if (ad->ad_rlen > 0) - if (fsetrsrcea(ad, ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen, 0) == -1) - err = -1; -#endif - if ((ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ - && !(--ad->ad_data_fork.adf_refcount)) { - if (ad_data_closefd(ad) < 0) - err = -1; - adf_lock_free(&ad->ad_data_fork); - if (ad->ad_resforkbuf) - free(ad->ad_resforkbuf); - ad->ad_resforkbuf = NULL; - ad->ad_rlen = 0; - } - break; - - default: - LOG(log_error, logtype_default, "ad_close: unknown AD version"); - errno = EIO; - return -1; + if ((adflags & ADFLAGS_RF) && (ad->ad_vers == AD_VERSION_EA)) { + if ((ad_reso_fileno(ad) != -1) + && !(--ad->ad_rfp->adf_refcount)) { + if (close(ad->ad_rfp->adf_fd) < 0) + err = -1; + ad->ad_rlen = 0; + ad_reso_fileno(ad) = -1; } } diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index e5bc2d10..2fd01d97 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -54,10 +54,6 @@ #include "ad_lock.h" -#ifndef MAX -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#endif /* ! MAX */ - #define ADEDOFF_MAGIC (0) #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC) #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION) @@ -104,13 +100,18 @@ static uid_t default_uid = -1; /* Forward declarations */ static int ad_mkrf(const char *path); -static int ad_header_read(struct adouble *ad, struct stat *hst); +static int ad_header_read(const char *path, struct adouble *ad, struct stat *hst); static int ad_header_upgrade(struct adouble *ad, const char *name); static int ad_mkrf_ea(const char *path); -static int ad_header_read_ea(struct adouble *ad, struct stat *hst); +static int ad_header_read_ea(const char *path, struct adouble *ad, struct stat *hst); static int ad_header_upgrade_ea(struct adouble *ad, const char *name); + +static const char *ad_path_osx(const char *path, int adflags); +static int ad_mkrf_osx(const char *path); + + static struct adouble_fops ad_adouble = { &ad_path, &ad_mkrf, @@ -120,13 +121,26 @@ static struct adouble_fops ad_adouble = { }; static struct adouble_fops ad_adouble_ea = { +#ifdef HAVE_EAFD &ad_path_ea, &ad_mkrf_ea, +#else + &ad_path_osx, + &ad_mkrf_osx, +#endif &ad_rebuild_adouble_header, &ad_header_read_ea, &ad_header_upgrade_ea, }; +static struct adouble_fops ad_osx = { + &ad_path_osx, + &ad_mkrf_osx, + &ad_rebuild_adouble_header, + &ad_header_read, + &ad_header_upgrade, +}; + static const struct entry entry_order2[ADEID_NUM_V2 + 1] = { {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT}, {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT}, @@ -150,6 +164,14 @@ static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = { {ADEID_COMMENT, ADEDOFF_COMMENT_EA, ADEDLEN_INIT}, {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI}, {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_EA, ADEDLEN_AFPFILEI}, + {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT}, + {0, 0, 0} +}; + +/* fallback for EAs */ +static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = { + {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI}, + {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT}, {0, 0, 0} }; @@ -246,12 +268,20 @@ static uint32_t get_eid(uint32_t eid) } /* ----------------------------------- */ -static int new_ad_header(const char *path, struct adouble *ad, int adflags) +static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp, int adflags) { const struct entry *eid; uint16_t ashort; struct stat st; + LOG(log_debug, logtype_default, "new_ad_header(\"%s\")", path); + + if (stp == NULL) { + stp = &st; + if (lstat(path, &st) != 0) + return -1; + } + ad->ad_magic = AD_MAGIC; ad->ad_version = ad->ad_vers & 0x0f0000; if (!ad->ad_version) { @@ -265,6 +295,8 @@ static int new_ad_header(const char *path, struct adouble *ad, int adflags) eid = entry_order2; else if (ad->ad_vers == AD_VERSION_EA) eid = entry_order_ea; + else if (ad->ad_vers == AD_VERSION2_OSX) + eid = entry_order_osx; else { return -1; } @@ -276,32 +308,31 @@ static int new_ad_header(const char *path, struct adouble *ad, int adflags) } /* put something sane in the directory finderinfo */ - if ((adflags & ADFLAGS_DIR)) { - /* set default view */ - ashort = htons(FINDERINFO_CLOSEDVIEW); - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort)); - } else { - /* set default creator/type fields */ - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4); - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4); - } - - /* make things invisible */ - if ((ad->ad_options & ADVOL_INVDOTS) && (*path == '.')) { - ashort = htons(ATTRBIT_INVISIBLE); - ad_setattr(ad, ashort); - ashort = htons(FINDERINFO_INVISIBLE); - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); - } + if (ad->ad_vers != AD_VERSION2_OSX) { + if ((adflags & ADFLAGS_DIR)) { + /* set default view */ + ashort = htons(FINDERINFO_CLOSEDVIEW); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort)); + } else { + /* set default creator/type fields */ + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4); + } - if (lstat(path, &st) < 0) - return -1; + /* make things invisible */ + if ((ad->ad_options & ADVOL_INVDOTS) && (*path == '.')) { + ashort = htons(ATTRBIT_INVISIBLE); + ad_setattr(ad, ashort); + ashort = htons(FINDERINFO_INVISIBLE); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); + } - /* put something sane in the date fields */ - ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime); - ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime); - ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime); - ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START); + /* put something sane in the date fields */ + ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime); + ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime); + ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime); + ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START); + } return 0; } @@ -325,8 +356,10 @@ static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries) len = ntohl( len ); buf += sizeof( len ); - if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) && - (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) { + if (eid + && eid < ADEID_MAX + && off < sizeof(ad->ad_data) + && (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) { ad->ad_eid[ eid ].ade_off = off; ad->ad_eid[ eid ].ade_len = len; } else if (!warning) { @@ -343,7 +376,7 @@ static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries) * NOTE: we're assuming that the resource fork is kept at the end of * the file. also, mmapping won't work for the hfs fs until it * understands how to mmap header files. */ -static int ad_header_read(struct adouble *ad, struct stat *hst) +static int ad_header_read(const char *path _U_, struct adouble *ad, struct stat *hst) { char *buf = ad->ad_data; uint16_t nentries; @@ -352,7 +385,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) struct stat st; /* read the header */ - if ((header_len = adf_pread( ad->ad_mdp, buf, sizeof(ad->ad_data), 0)) < 0) { + if ((header_len = adf_pread( ad->ad_mdp, buf, AD_DATASZ, 0)) < 0) { return -1; } if (header_len < AD_HEADER_LEN) { @@ -418,7 +451,77 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) return 0; } -static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_) +/* Read an ._ file, only uses the resofork, finderinfo is taken from EA */ +static int ad_header_read_osx(const char *path _U_, struct adouble *ad, struct stat *hst) +{ + EC_INIT; + struct adouble adosx; + char *buf = &adosx.ad_data[0]; + uint16_t nentries; + int len; + ssize_t header_len; + struct stat st; + + memset(buf, 0, sizeof(adosx.ad_data)); + + /* read the header */ + EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) ); + + if (header_len < AD_HEADER_LEN) { + errno = EIO; + return -1; + } + + memcpy(&adosx.ad_magic, buf, sizeof(adosx.ad_magic)); + memcpy(&adosx.ad_version, buf + ADEDOFF_VERSION, sizeof(adosx.ad_version)); + adosx.ad_magic = ntohl(adosx.ad_magic); + adosx.ad_version = ntohl(adosx.ad_version); + + if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) { + LOG(log_error, logtype_afpd, "ad_header_read_osx: can't parse AppleDouble header"); + errno = EIO; + return -1; + } + + memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries )); + nentries = ntohs(nentries); + len = nentries * AD_ENTRY_LEN; + + if (len + AD_HEADER_LEN > sizeof(adosx.ad_data)) + len = sizeof(adosx.ad_data) - AD_HEADER_LEN; + + buf += AD_HEADER_LEN; + if (len > header_len - AD_HEADER_LEN) { + LOG(log_error, logtype_afpd, "ad_header_read_osx: can't read entry info."); + errno = EIO; + return -1; + } + + nentries = len / AD_ENTRY_LEN; + parse_entries(&adosx, buf, nentries); + + if (ad_getentryoff(&adosx, ADEID_RFORK) == 0 + || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data) + || ad_getentryoff(&adosx, ADEID_RFORK) > header_len + ) { + LOG(log_error, logtype_afpd, "ad_header_read_osx: problem with rfork entry offset."); + errno = EIO; + return -1; + } + + if (hst == NULL) { + hst = &st; + EC_NEG1( fstat(ad_reso_fileno(ad), &st) ); + } + + ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(&adosx, ADEID_RFORK)); + ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK); + +EC_CLEANUP: + EC_EXIT; +} + +static int ad_header_read_ea(const char *path, struct adouble *ad, struct stat *hst _U_) { uint16_t nentries; int len; @@ -426,8 +529,17 @@ static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_) char *buf = ad->ad_data; /* read the header */ - if ((header_len = sys_fgetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA)) < 1) { - LOG(log_debug, logtype_default, "ad_header_read_ea: %s (%u)", strerror(errno), errno); + if (ad_meta_fileno(ad) == -1 && ad->ad_adflags && ADFLAGS_RDWR) { + LOG(log_error, logtype_default, "ad_header_read_ea: need filedescriptor for rw access"); + return -1; + } + + if (ad_meta_fileno(ad) != -1) + header_len = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA); + else + header_len = sys_lgetxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA); + if (header_len < 1) { + LOG(log_debug, logtype_default, "ad_header_read_ea: %s", strerror(errno)); return -1; } @@ -469,37 +581,13 @@ static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_) return 0; } -static int ad_read_rf_ea(struct adouble *ad) -{ - EC_INIT; - ssize_t rlen; - - if ((rlen = sys_fgetxattr(ad_data_fileno(ad), AD_EA_RESO, NULL, 0)) <= 0) { - switch (errno) { - case ENOATTR: - case ENOENT: - return 0; - default: - LOG(log_error, logtype_default, "ad_refresh_rsrc_len_ea: %s", strerror(errno)); - return -1; - } - } - - if (ad->ad_resforkbuf == NULL) { - EC_NULL_LOG( ad->ad_resforkbuf = malloc(rlen) ); - } else { - if (rlen > ad->ad_rlen) { - EC_ZERO_LOG( ad->ad_resforkbuf = realloc(ad->ad_resforkbuf, rlen) ); - } - } - ad->ad_rlen = rlen; - - EC_NEG1_LOG( sys_fgetxattr(ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen) ); - -EC_CLEANUP: - EC_EXIT; -} - +/*! + * Takes a path to an AppleDouble file and creates the parrent .AppleDouble directory + * + * Example: + * path: "/path/.AppleDouble/file" + * => mkdir("/path/.AppleDouble/") (in ad_mkdir()) + */ static int ad_mkrf(const char *path) { char *slash; @@ -524,6 +612,11 @@ static int ad_mkrf_ea(const char *path _U_) return 0; } +static int ad_mkrf_osx(const char *path _U_) +{ + return 0; +} + /* ---------------- if we are root change path user/ group It can be a native function for BSD cf. FAQ.Q10 @@ -608,10 +701,12 @@ static int ad2openflags(int adflags) if (adflags & ADFLAGS_RDWR) oflags |= O_RDWR; - if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) - oflags |= O_RDWR; - else - oflags |= O_RDONLY; + if (adflags & ADFLAGS_RDONLY) { + if (adflags & ADFLAGS_SETSHRMD) + oflags |= O_RDWR; + else + oflags |= O_RDONLY; + } if (adflags & ADFLAGS_CREATE) oflags |= O_CREAT; if (adflags & ADFLAGS_EXCL) @@ -642,7 +737,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble return -1; } /* it's not new anymore */ - ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT ); + ad->ad_data_fork.adf_flags &= ~( O_TRUNC | O_CREAT ); ad->ad_data_fork.adf_refcount++; return 0; } @@ -695,6 +790,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble return 0; } +/* TODO: error handling */ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adouble *ad) { struct stat st_dir; @@ -713,7 +809,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou errno = EACCES; return -1; } - ad_refresh(ad); + ad_refresh(path, ad); /* it's not new anymore */ ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT ); ad->ad_mdp->adf_refcount++; @@ -747,7 +843,6 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou return ad_error(ad, adflags); /* * We're expecting to create a new adouble header file here - * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) */ LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file", fullpathname(path)); @@ -757,7 +852,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou if ((ad->ad_options & ADVOL_UNIXPRIV)) admode = mode; admode = ad_hf_mode(admode); - if (errno == ENOENT) { + if ((errno == ENOENT) && (ad->ad_vers != AD_VERSION2_OSX)) { if (ad->ad_ops->ad_mkrf( ad_p) < 0) { return ad_error(ad, adflags); } @@ -794,12 +889,12 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou } } - ad->ad_mdp->adf_refcount = 1; adf_lock_init(ad->ad_mdp); + ad->ad_mdp->adf_refcount = 1; if ((ad->ad_mdp->adf_flags & ( O_TRUNC | O_CREAT ))) { /* This is a new adouble header file, create it */ - if (new_ad_header(path, ad, adflags) < 0) { + if (new_ad_header(ad, path, pst, adflags) < 0) { int err = errno; /* the file is already deleted, perm, whatever, so return an error */ ad_close(ad, adflags); @@ -809,7 +904,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou ad_flush(ad); } else { /* Read the adouble header in and parse it.*/ - if (ad->ad_ops->ad_header_read( ad , pst) < 0 + if (ad->ad_ops->ad_header_read(path, ad, pst) < 0 || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) { int err = errno; ad_close(ad, adflags); @@ -821,40 +916,49 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou return 0; } +/* TODO: error handling */ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad) { ssize_t rforklen; - int oflags = O_NOFOLLOW; + int oflags; LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\", %04o)", path, mode); - oflags |= ad2openflags(adflags) & ~(O_CREAT | O_TRUNC); + oflags = O_NOFOLLOW | (ad2openflags(adflags) & ~(O_CREAT | O_TRUNC)); - if (ad_data_fileno(ad) != -1) { + if (ad_meta_fileno(ad) != -1) { /* the file is already open, but we want write access: */ if ((adflags & ADFLAGS_RDWR) && /* and it was already denied: */ - (ad->ad_data_fork.adf_flags & O_RDONLY)) { + (ad->ad_mdp->adf_flags & O_RDONLY)) { LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s", fullpathname(path), strerror(errno)); errno = EACCES; return -1; } - /* it's not new anymore */ ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT ); + ad->ad_mdp->adf_refcount++; } else { - if ((ad_data_fileno(ad) = open(path, oflags)) == -1) - goto error; - ad->ad_data_fork.adf_flags = oflags; - adf_lock_init(&ad->ad_data_fork); + if (adflags & ADFLAGS_RDWR) { + /* Fo a RDONLY adouble we just use sys_lgetxattr instead if sys_fgetxattr */ + LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening for base file for meta adouble EA", path); + if ((ad_meta_fileno(ad) = open(path, oflags)) == -1) + goto error; + ad->ad_mdp->adf_flags = oflags; + } + ad->ad_mdp->adf_refcount = 1; } /* Read the adouble header in and parse it.*/ - if (ad->ad_ops->ad_header_read(ad, NULL) != 0) { + if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) { + LOG(log_error, logtype_default, "ad_open_hf_ea: no EA adouble"); + if (!(adflags & ADFLAGS_CREATE)) goto error; + LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): creating metadata EA", path); + /* It doesnt exist, EPERM or another error */ if (!(errno == ENOATTR || errno == ENOENT)) { LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno)); @@ -862,7 +966,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble } /* Create one */ - if (new_ad_header(path, ad, adflags) < 0) { + if (new_ad_header(ad, path, NULL, adflags) < 0) { LOG(log_error, logtype_default, "ad_open_hf_ea: can't create new header: %s", fullpathname(path)); goto error; @@ -872,17 +976,16 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path); } - ad->ad_data_fork.adf_refcount++; - - if ((rforklen = sys_fgetxattr(ad_data_fileno(ad), AD_EA_RESO, NULL, 0)) > 0) - ad->ad_rlen = rforklen; + /* TODO: ad_rlen calculation */ + ad->ad_rlen = 0; return 0; error: - if (ad_data_fileno(ad) != -1) { - close(ad_data_fileno(ad)); - ad_data_fileno(ad) = -1; + if (ad_meta_fileno(ad) != -1) { + close(ad_meta_fileno(ad)); + ad_meta_fileno(ad) = -1; + ad->ad_mdp->adf_refcount = 0; } return ad_error(ad, adflags); } @@ -908,6 +1011,9 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a break; } + if (ret != 0) { + return ad_error(ad, adflags); + } return ret; } @@ -922,41 +1028,87 @@ static int ad_open_rf(const char *path, int adflags, int mode, struct adouble *a { EC_INIT; int oflags; + int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF); ssize_t rlen; +#ifndef HAVE_EAFD + const char *rfpath; + struct stat st; +#endif if (ad->ad_vers != AD_VERSION_EA) return 0; LOG(log_debug, logtype_default, "ad_open_rf(\"%s\", %04o)", path, mode); - oflags |= ad2openflags(adflags) & ~(O_CREAT | O_TRUNC); + oflags = O_NOFOLLOW | (ad2openflags(adflags) & ~O_CREAT); - if (ad_data_fileno(ad) != -1) { + if (ad_reso_fileno(ad) != -1) { /* the file is already open, but we want write access: */ - if ((adflags & ADFLAGS_RDWR) && + if ((adflags & ADFLAGS_RDWR) /* and it was already denied: */ - (ad->ad_data_fork.adf_flags & O_RDONLY)) { - LOG(log_error, logtype_default, "ad_open_rf(%s): rw request for ro file: %s", - fullpathname(path), strerror(errno)); + && (ad->ad_rfp->adf_flags & O_RDONLY)) { errno = EACCES; return -1; } - } else { - EC_NEG1( (ad_data_fileno(ad) = open(path, oflags)) ); - ad->ad_data_fork.adf_flags = oflags; - adf_lock_init(&ad->ad_data_fork); -#ifndef HAVE_EAFD - EC_ZERO_LOG( ad_read_rf_ea(ad) ); -#endif + ad->ad_rfp->adf_flags &= ~( O_TRUNC | O_CREAT ); + ad->ad_rfp->adf_refcount++; + return 0; + } +#ifdef HAVE_EAFD + if ((ad_reso_fileno(ad) = sys_getxattrfd(path, oflags)) == -1) { + if (!(adflags & ADFLAGS_CREATE)) + EC_FAIL; + oflags |= O_CREAT; + EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(path, oflags, 0666) ); + } +#else + EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags) ); + if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) { + if (!(adflags & ADFLAGS_CREATE)) + EC_FAIL; + oflags |= O_CREAT; + EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) ); + LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"", + path, rfpath); } +#endif - ad->ad_data_fork.adf_refcount++; + ad->ad_rfp->adf_refcount = 1; + ad->ad_rfp->adf_flags = oflags; + +#ifndef HAVE_EAFD + EC_ZERO_LOG( fstat(ad_reso_fileno(ad), &st) ); + if (ad->ad_rfp->adf_flags & O_CREAT) { + /* This is a new adouble header file, create it */ + LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"", + path, rfpath); +// EC_NEG1_LOG( new_ad_header(ad, rfpath, &st, adflags) ); + LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"", + path, rfpath); + ad_flush(ad); + } else { + /* Read the adouble header */ + LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"", + path, rfpath); + EC_NEG1_LOG( ad_header_read_osx(NULL, ad, &st) ); + } +#endif EC_CLEANUP: if (ret != 0) { + if (ad_reso_fileno(ad) != -1) { + close(ad_reso_fileno(ad)); + ad_reso_fileno(ad) = -1; + ad->ad_rfp->adf_refcount = 0; + } + int err = errno; + (void)ad_close(ad, closeflags); + errno = err; ad->ad_rlen = 0; - ad->ad_adflags &= ~ADFLAGS_RF; } + + LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): END", path); + EC_EXIT; } @@ -969,6 +1121,32 @@ const char *ad_path_ea( const char *path, int adflags _U_) return path; } +const char *ad_path_osx(const char *path, int adflags _U_) +{ + static char pathbuf[ MAXPATHLEN + 1]; + char c, *slash, buf[MAXPATHLEN + 1]; + + if (!strcmp(path,".")) { + /* fixme */ + getcwd(buf, MAXPATHLEN); + } + else { + strlcpy(buf, path, MAXPATHLEN +1); + } + if (NULL != ( slash = strrchr( buf, '/' )) ) { + c = *++slash; + *slash = '\0'; + strlcpy( pathbuf, buf, MAXPATHLEN +1); + *slash = c; + } else { + pathbuf[ 0 ] = '\0'; + slash = buf; + } + strlcat( pathbuf, "._", MAXPATHLEN +1); + strlcat( pathbuf, slash, MAXPATHLEN +1); + return pathbuf; +} + /* * Put the .AppleDouble where it needs to be: * @@ -1129,31 +1307,23 @@ static void ad_init_func(struct adouble *ad) case AD_VERSION_EA: ad->ad_ops = &ad_adouble_ea; ad->ad_rfp = &ad->ad_resource_fork; - ad->ad_mdp = &ad->ad_resource_fork; + ad->ad_mdp = &ad->ad_data_fork; break; default: AFP_PANIC("ad_init: unknown AD version"); } + ad_data_fileno(ad) = -1; ad_reso_fileno(ad) = -1; ad_meta_fileno(ad) = -1; - memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); - ad->ad_rlen = 0; ad->ad_refcount = 1; - ad->ad_open_forks = 0; - ad->ad_resource_fork.adf_refcount = 0; - ad->ad_resforkbuf = NULL; - ad->ad_data_fork.adf_refcount = 0; - ad->ad_data_fork.adf_syml = 0; - ad->ad_inited = 0; -// ad->ad_maxeafssize = 3500; /* FIXME: option from vol */ - ad->ad_maxeafssize = 0; /* no limit */ return; } void ad_init_old(struct adouble *ad, int flags, int options) { + memset(ad, 0, sizeof(struct adouble)); ad->ad_vers = flags; ad->ad_options = options; ad_init_func(ad); @@ -1161,6 +1331,7 @@ void ad_init_old(struct adouble *ad, int flags, int options) void ad_init(struct adouble *ad, const struct vol * restrict vol) { + memset(ad, 0, sizeof(struct adouble)); ad->ad_vers = vol->v_adouble; ad->ad_options = vol->v_ad_options; ad_init_func(ad); @@ -1182,17 +1353,30 @@ void ad_init(struct adouble *ad, const struct vol * restrict vol) * * @param ad (rw) pointer to struct adouble * @param path (r) Path to file or directory - * @param adflags (r) ADFLAGS_DF: open data fork \n - * ADFLAGS_RF: open ressource fork \n - * ADFLAGS_HF: open header (metadata) file \n - * ADFLAGS_NOHF: it's not an error if header file couldn't be created \n - * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags \n + * @param adflags (r) Flags specifying which fork to open, can be or'd: + * ADFLAGS_DF: open data fork + * ADFLAGS_RF: open ressource fork + * ADFLAGS_HF: open header (metadata) file + * ADFLAGS_NOHF: it's not an error if header file couldn't be created + * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags + * + * Access mode for the forks: + * ADFLAGS_RDONLY: open read only + * ADFLAGS_RDWR: open read write + * + * Creation flags: + * ADFLAGS_CREATE: create if not existing + * ADFLAGS_TRUNC: truncate + * + * Special flags: + * ADFLAGS_CHECK_OF: check for open forks from us and other afpd's + * ADFLAGS_SETSHRMD: this adouble struct will be used to set sharemode locks. + * This basically results in the files being opened RW instead of RDONLY. + * @param mode (r) mode used with O_CREATE + * * The open mode flags (rw vs ro) have to take into account all the following requirements: * - we remember open fds for files because me must avoid a single close releasing fcntl locks for other * fds of the same file - * - a file may be opened first ro, then rw and theres no way to upgrade this -> fork.c always opens rw - * ADFLAGS_CHECK_OF: check for open forks from us and other afpd's - * @param mode (r) mode used with O_CREATE * * @returns 0 on success, any other value indicates an error */ @@ -1205,6 +1389,14 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...) LOG(log_debug, logtype_default, "ad_open(\"%s\", %s)", fullpathname(path), adflags2logstr(adflags)); + if (adflags & ADFLAGS_CHECK_OF) + /* Checking for open forks requires sharemode lock support (ie RDWR instead of RDONLY) */ + adflags |= ADFLAGS_SETSHRMD; + + if ((ad->ad_vers == AD_VERSION_EA) && (adflags & ADFLAGS_SETSHRMD)) + /* adouble:ea sets sharemode locks on the datafork */ + adflags |= ADFLAGS_DF; + if (ad->ad_inited != AD_INITED) { ad->ad_adflags = adflags; ad->ad_inited = AD_INITED; @@ -1221,14 +1413,21 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...) if (adflags & ADFLAGS_DF) { EC_ZERO( ad_open_df(path, adflags, mode, ad) ); + ad->ad_adflags |= ADFLAGS_DF; } if (adflags & ADFLAGS_HF) { EC_ZERO( ad_open_hf(path, adflags, mode, ad) ); + ad->ad_adflags |= ADFLAGS_HF; } if (adflags & ADFLAGS_RF) { EC_ZERO( ad_open_rf(path, adflags, mode, ad) ); + ad->ad_adflags |= ADFLAGS_RF; + } + + if (adflags & ADFLAGS_CHECK_OF) { + ad->ad_open_forks |= ad_openforks(ad, ad->ad_open_forks); } EC_CLEANUP: @@ -1251,9 +1450,10 @@ int ad_metadata(const char *name, int flags, struct adouble *adp) uid_t uid; int ret, err, oflags; - oflags = (flags & ADFLAGS_DIR) | ADFLAGS_HF | ADFLAGS_RDONLY; + /* Sanitize flags */ + oflags = (flags & (ADFLAGS_CHECK_OF | ADFLAGS_DIR)) | ADFLAGS_HF | ADFLAGS_RDONLY; - if ((ret = ad_open(adp, name, oflags | ADFLAGS_SETSHRMD)) < 0 && errno == EACCES) { + if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) { uid = geteuid(); if (seteuid(0)) { LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno)); @@ -1270,15 +1470,6 @@ int ad_metadata(const char *name, int flags, struct adouble *adp) errno = err; } - if ((ret == 0) && (ADFLAGS_CHECK_OF & flags)) { - /* - we need to check if the file is open by another process. - it's slow so we only do it if we have to: - - it's requested. - - we don't already have the answer! - */ - adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks); - } return ret; } @@ -1317,22 +1508,44 @@ exit: } -int ad_refresh(struct adouble *ad) +int ad_refresh(const char *path, struct adouble *ad) { switch (ad->ad_vers) { case AD_VERSION2: if (ad_meta_fileno(ad) == -1) return -1; - return ad->ad_ops->ad_header_read(ad, NULL); + return ad->ad_ops->ad_header_read(NULL, ad, NULL); break; case AD_VERSION_EA: - if (ad_data_fileno(ad) == -1) - return -1; -#ifndef HAVE_EAFD - if (ad_read_rf_ea(ad) != 0) - return -1; +#ifdef HAVE_EAFD + if (AD_META_OPEN(ad)) { + if (ad_data_fileno(ad) == -1) + return -1; + // TODO: read meta EA + } + + if (AD_RSRC_OPEN(ad)) { + if (ad_reso_fileno(ad) == -1) + return -1; + ssize_t len; + if ((len = fstat(ad_reso_fileno(ad))) == -1) + return -1; + ad->ad_rlen = len; + } +#else + if (AD_META_OPEN(ad)) { + if (ad_data_fileno(ad) == -1) + return -1; + // TODO: read meta EA + } + if (AD_RSRC_OPEN(ad)) { + if (ad_reso_fileno(ad) == -1) + return -1; + if (ad_header_read_osx(path, ad, NULL) < 0) + return -1; + } #endif - return ad->ad_ops->ad_header_read(ad, NULL); + return ad->ad_ops->ad_header_read(path, ad, NULL); break; default: return -1; diff --git a/libatalk/adouble/ad_read.c b/libatalk/adouble/ad_read.c index 9974293f..5766ad9d 100644 --- a/libatalk/adouble/ad_read.c +++ b/libatalk/adouble/ad_read.c @@ -63,7 +63,7 @@ ssize_t ad_read( struct adouble *ad, const uint32_t eid, off_t off, char *buf, c { ssize_t cc; ssize_t rlen; - off_t r_off; + off_t r_off = 0; /* We're either reading the data fork (and thus the data file) * or we're reading anything else (and thus the header file). */ @@ -85,38 +85,18 @@ ssize_t ad_read( struct adouble *ad, const uint32_t eid, off_t off, char *buf, c switch (ad->ad_vers) { case AD_VERSION2: - r_off = ad_getentryoff(ad, eid) + off; - if (( cc = adf_pread( &ad->ad_resource_fork, buf, buflen, r_off )) < 0 ) - return( -1 ); - /* - * We've just read in bytes from the disk that we read earlier - * into ad_data. If we're going to write this buffer out later, - * we need to update ad_data. - * FIXME : always false? - */ - if (r_off < ad_getentryoff(ad, ADEID_RFORK)) { - if ( ad->ad_resource_fork.adf_flags & O_RDWR ) { - memcpy(buf, ad->ad_data + r_off, - MIN(sizeof( ad->ad_data ) - r_off, cc)); - } else { - memcpy(ad->ad_data + r_off, buf, - MIN(sizeof( ad->ad_data ) - r_off, cc)); - } - } - break; - - case AD_VERSION_EA: #ifndef HAVE_EAFD - if (off > ad->ad_rlen) { - errno = ERANGE; - return -1; - } - if ((off + buflen) > ad->ad_rlen) - cc = off + buflen - ad->ad_rlen; - memcpy(buf, ad->ad_resforkbuf + off, cc); + case AD_VERSION_EA: #endif + r_off = ad_getentryoff(ad, eid) + off; + break; + default: + r_off = 0; break; } + + if (( cc = adf_pread( &ad->ad_resource_fork, buf, buflen, r_off )) < 0 ) + return( -1 ); } return( cc ); diff --git a/libatalk/adouble/ad_write.c b/libatalk/adouble/ad_write.c index 5ebc7395..f5a5c935 100644 --- a/libatalk/adouble/ad_write.c +++ b/libatalk/adouble/ad_write.c @@ -73,49 +73,22 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha } cc = adf_pwrite(&ad->ad_data_fork, buf, buflen, off); } else if ( eid == ADEID_RFORK ) { - switch (ad->ad_vers) { - case AD_VERSION2: - - if ( end ) { - if ( fstat( ad_data_fileno(ad), &st ) < 0 ) - return( -1 ); - off = st.st_size - off -ad_getentryoff(ad, eid); - } + if (end) { + if (fstat( ad_reso_fileno(ad), &st ) < 0) + return(-1); + off = st.st_size - off - ad_getentryoff(ad, eid); + } +#ifdef HAVE_EAFD + if (ad->ad_vers == AD_VERSION_EA) { + r_off = 0; + } else +#endif r_off = ad_getentryoff(ad, eid) + off; - cc = adf_pwrite(&ad->ad_resource_fork, buf, buflen, r_off); - - /* sync up our internal buffer FIXME always false? */ - if (r_off < ad_getentryoff(ad, ADEID_RFORK)) - memcpy(ad->ad_data + r_off, buf, MIN(sizeof(ad->ad_data) -r_off, cc)); - if ( ad->ad_rlen < off + cc ) - ad->ad_rlen = off + cc; - break; - - case AD_VERSION_EA: -#ifndef HAVE_EAFD - LOG(log_debug, logtype_default, "ad_write: off: %ju, size: %zu, eabuflen: %zu", - (uintmax_t)off, buflen, ad->ad_rlen); - if (ad->ad_rlen == 0) { - EC_NULL_LOG( ad->ad_resforkbuf = malloc(off + buflen) ); - ad->ad_rlen = off + buflen; - } else if ((off + buflen) > ad->ad_rlen) { - EC_NULL_LOG( ad->ad_resforkbuf = realloc(ad->ad_resforkbuf, off + buflen) ); - ad->ad_rlen = off + buflen; - } - memcpy(ad->ad_resforkbuf + off, buf, buflen); - if (ad->ad_rlen > 0) - if (fsetrsrcea(ad, ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen, 0) == -1) - ret = -1; - cc = buflen; - LOG(log_debug, logtype_default, "ad_write: off: %ju, size: %zu, eabuflen: %zu", - (uintmax_t)off, buflen, ad->ad_rlen); -#endif - break; + cc = adf_pwrite(&ad->ad_resource_fork, buf, buflen, r_off); - default: - return -1; - } + if ( ad->ad_rlen < off + cc ) + ad->ad_rlen = off + cc; } else { return -1; /* we don't know how to write if it's not a ressource or data fork */ } diff --git a/libatalk/vfs/extattr.c b/libatalk/vfs/extattr.c index 853b7f2f..9883563b 100644 --- a/libatalk/vfs/extattr.c +++ b/libatalk/vfs/extattr.c @@ -88,6 +88,31 @@ static const char *prefix(const char *uname) #endif } +int sys_getxattrfd(const char *path, const char *uname, int oflag, ...) +{ +#if defined HAVE_ATTROPEN + int eafd; + va_list args; + mode_t mode; + + if (oflags & O_CREAT) { + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + } + + if (oflags & O_CREAT) + eafd = attropen(path, uname, oflag, mode); + else + eafd = attropen(path, uname, oflag); + + return eafd; +#else + errno = ENOSYS; + return -1; +#endif +} + ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size) { const char *name = prefix(uname); -- 2.39.2