diff --git a/include/atalk/ea.h b/include/atalk/ea.h index b9c27caefc..08d823535f 100644 --- a/include/atalk/ea.h +++ b/include/atalk/ea.h @@ -88,8 +88,14 @@ enum { /* Names for our Extended Attributes adouble data */ #define AD_EA_META "org.netatalk.Metadata" +#ifdef __APPLE__ +#define AD_EA_RESO "com.apple.ResourceFork" +#define EA_FINFO "com.apple.FinderInfo" +#define NOT_NETATALK_EA(a) (strcmp((a), AD_EA_META) != 0) && (strcmp((a), AD_EA_RESO) != 0) && (strcmp((a), EA_FINFO) != 0) +#else #define AD_EA_RESO "org.netatalk.ResourceFork" #define NOT_NETATALK_EA(a) (strcmp((a), AD_EA_META) != 0) && (strcmp((a), AD_EA_RESO) != 0) +#endif /**************************************************************************************** * Wrappers for native EA functions taken from Samba diff --git a/libatalk/adouble/ad_flush.c b/libatalk/adouble/ad_flush.c index 6169eadeec..3d33476cb4 100644 --- a/libatalk/adouble/ad_flush.c +++ b/libatalk/adouble/ad_flush.c @@ -313,12 +313,20 @@ static int ad_flush_hf(struct adouble *ad) break; case AD_VERSION_EA: if (AD_META_OPEN(ad)) { +#ifdef __APPLE__ + char * FinderInfo; + char NativeFinderInfo[32]={'\0'}; + FinderInfo = ad_entry(ad, ADEID_FINDERI); + memcpy(NativeFinderInfo, FinderInfo,32); +#endif if (ad->ad_adflags & ADFLAGS_DIR) { EC_NEG1_LOG( cwd = open(".", O_RDONLY) ); EC_NEG1_LOG( fchdir(ad_data_fileno(ad)) ); ret = sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0); - +#ifdef __APPLE__ + sys_lsetxattr(".", EA_FINFO, NativeFinderInfo, 32, 0); +#endif if (ret != 0) { if (errno != EPERM) EC_FAIL; @@ -335,6 +343,9 @@ static int ad_flush_hf(struct adouble *ad) become_root(); ret = sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0); +#ifdef __APPLE__ + sys_lsetxattr(".", EA_FINFO, NativeFinderInfo, 32, 0); +#endif unbecome_root(); if (ret != 0) { @@ -348,6 +359,11 @@ static int ad_flush_hf(struct adouble *ad) cwd = -1; } else { EC_ZERO_LOG( sys_fsetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) ); +#ifdef __APPLE__ + /* write back finderinfo to macos native ea, but only if we aren't creating a new adouble ea for netatalk */ + if (!(ad->ad_mdp->adf_flags & O_CREAT)) + EC_ZERO_LOG( sys_fsetxattr(ad_data_fileno(ad), EA_FINFO, NativeFinderInfo, 32, 0) ); +#endif } } break; diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index dd151f0e0e..409fcc5af0 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -108,7 +108,7 @@ static int ad_mkrf(const char *path); static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst); static int ad_header_upgrade(struct adouble *ad, const char *name); -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) static int ad_mkrf_ea(const char *path); #endif static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst); @@ -126,7 +126,7 @@ static struct adouble_fops ad_adouble = { }; static struct adouble_fops ad_adouble_ea = { -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) &ad_path_ea, &ad_mkrf_ea, #else @@ -1249,6 +1249,11 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode _U_, struct ado } } +#ifdef __APPLE__ + char * FinderInfo; + char NativeFinderInfo[32]={'\0'}; +#endif + /* Read the adouble header in and parse it.*/ if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) { if (!(adflags & ADFLAGS_CREATE)) { @@ -1272,10 +1277,23 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode _U_, struct ado /* Create one */ EC_NEG1_LOG(new_ad_header(ad, path, NULL, adflags)); ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */ +#ifdef __APPLE__ + /* read in finderinfo from macos */ + FinderInfo = ad_entry(ad, ADEID_FINDERI); + if (sys_getxattr(path,EA_FINFO,NativeFinderInfo,32)) + memcpy(FinderInfo, NativeFinderInfo,32); +#endif ad_flush(ad); LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): created metadata EA", path); } +#ifdef __APPLE__ + /* read in finderinfo from macos */ + FinderInfo = ad_entry(ad, ADEID_FINDERI); + if (sys_getxattr(path,EA_FINFO,NativeFinderInfo,32)) + memcpy(FinderInfo, NativeFinderInfo,32); +#endif + if (ad_meta_fileno(ad) != -1) ad->ad_mdp->adf_refcount++; ad->ad_rlen = ad_reso_size(path, adflags, ad); @@ -1393,7 +1411,7 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble int oflags; int opened = 0; int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF); -#ifndef HAVE_EAFD +#ifndef SOLARIS const char *rfpath; struct stat st; #endif @@ -1416,7 +1434,7 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble EC_NEG1_LOG( ad->ad_rlen = ad_reso_size(path, adflags, ad)); goto EC_CLEANUP; } -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) if (ad_meta_fileno(ad) < 0) EC_FAIL; if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) { @@ -1722,8 +1740,14 @@ const char *ad_path_osx(const char *path, int adflags _U_) pathbuf[ 0 ] = '\0'; slash = buf; } +#ifdef __APPLE__ + /* macos abstracts the resource fork as "filename/..namedfork/rsrc" which we can treat as a file */ + strlcat( pathbuf, buf, MAXPATHLEN +1); + strlcat( pathbuf, "/..namedfork/rsrc", MAXPATHLEN +1); +#else strlcat( pathbuf, "._", MAXPATHLEN +1); strlcat( pathbuf, slash, MAXPATHLEN +1); +#endif return pathbuf; } diff --git a/libatalk/vfs/extattr.c b/libatalk/vfs/extattr.c index a9cd0bd141..ee36c1a16e 100644 --- a/libatalk/vfs/extattr.c +++ b/libatalk/vfs/extattr.c @@ -82,7 +82,7 @@ static char attr_name[256 +5] = "user."; static const char *prefix(const char *uname) { -#if defined(SOLARIS) +#if defined(SOLARIS) || defined(__APPLE__) return uname; #else strlcpy(attr_name +5, uname, 256); @@ -124,6 +124,11 @@ ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t s return getxattr(path, name, value, size); #else int options = 0; +#ifdef __APPLE__ + /* macOS only returns EA size if value is NULL */ + if (size == 0) + value = NULL; +#endif return getxattr(path, name, value, size, 0, options); #endif #elif defined(HAVE_GETEA) @@ -182,6 +187,11 @@ ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size) return fgetxattr(filedes, name, value, size); #else int options = 0; +#ifdef __APPLE__ + /* macOS only returns EA size if value is NULL */ + if (size == 0) + value = NULL; +#endif return fgetxattr(filedes, name, value, size, 0, options); #endif #elif defined(HAVE_FGETEA) @@ -240,6 +250,11 @@ ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t return lgetxattr(path, name, value, size); #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT) int options = XATTR_NOFOLLOW; +#ifdef __APPLE__ + /* macOS only returns EA size if value is NULL */ + if (size == 0) + value = NULL; +#endif return getxattr(path, name, value, size, 0, options); #elif defined(HAVE_LGETEA) return lgetea(path, name, value, size); @@ -444,7 +459,10 @@ static ssize_t remove_user(ssize_t ret, char *list, size_t size) char *ptr; char *ptr1; ssize_t ptrsize; - +#ifdef __APPLE__ + /* macOS doesn't prefix EAs with "user." */ + return ret; +#else if (ret <= 0 || size == 0) return ret; ptrsize = ret; @@ -461,6 +479,7 @@ static ssize_t remove_user(ssize_t ret, char *list, size_t size) ptr1 += len; } return ptr -list; +#endif } #endif diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index ec52eee34f..99201a77d2 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -552,7 +552,7 @@ static int RF_deletefile_ea(VFS_FUNC_ARGS_DELETEFILE) } static int RF_copyfile_ea(VFS_FUNC_ARGS_COPYFILE) { -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) /* the EA VFS module does this all for us */ return 0; #endif @@ -575,8 +575,14 @@ static int RF_copyfile_ea(VFS_FUNC_ARGS_COPYFILE) EC_NULL(dup2 = strdup(src)); EC_NULL(dir = dirname(dup2)); EC_NULL(s = bfromcstr(dir)); +#ifdef __APPLE__ + EC_ZERO(bcatcstr(s, "/")); + EC_ZERO(bcatcstr(s, name)); + EC_ZERO(bcatcstr(s, "/..namedfork/rsrc")); +#else EC_ZERO(bcatcstr(s, "/._")); EC_ZERO(bcatcstr(s, name)); +#endif /* build dst path to ._file*/ EC_NULL(dup4 = strdup(dst)); @@ -585,8 +591,14 @@ static int RF_copyfile_ea(VFS_FUNC_ARGS_COPYFILE) EC_NULL(dup3 = strdup(dst)); EC_NULL(dir = dirname(dup3)); EC_NULL(d = bfromcstr(dir)); +#ifdef __APPLE__ + EC_ZERO(bcatcstr(d, "/")); + EC_ZERO(bcatcstr(d, name)); + EC_ZERO(bcatcstr(d, "/..namedfork/rsrc")); +#else EC_ZERO(bcatcstr(d, "/._")); EC_ZERO(bcatcstr(d, name)); +#endif if (copy_file(sfd, cfrombstr(s), cfrombstr(d), 0666) != 0) { switch (errno) { @@ -864,7 +876,7 @@ void initvol_vfs(struct vol *vol) vol->ad_path = ad_path; } else { vol->vfs_modules[0] = &netatalk_adouble_ea; -#ifdef HAVE_EAFD +#if defined(HAVE_EAFD) && defined(SOLARIS) vol->ad_path = ad_path_ea; #else vol->ad_path = ad_path_osx; diff --git a/meson.build b/meson.build index 4d09782619..585e5cc58e 100644 --- a/meson.build +++ b/meson.build @@ -1177,6 +1177,10 @@ else have_ea = false endif +if host_os == 'darwin' + cdata.set('HAVE_EAFD', 1) +endif + if have_ea netatalk_ea += ' | sys"' ea_summary += ', filesystem EA' diff --git a/test/testsuite/adoublehelper.c b/test/testsuite/adoublehelper.c index 14d9eea3c8..5b2c893296 100644 --- a/test/testsuite/adoublehelper.c +++ b/test/testsuite/adoublehelper.c @@ -251,7 +251,7 @@ static int chmod_unix_adouble(char *path,char *name, int mode) int chmod_unix_meta(char *path, char *name, char *file, mode_t mode) { if (adouble == AD_EA) { -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) sprintf(temp, "runat '%s/%s/%s' chmod 0%o %s", path, name, file, mode, AD_EA_META); if (!Quiet) { fprintf(stdout, "%s\n", temp); @@ -288,7 +288,7 @@ int chmod_unix_meta(char *path, char *name, char *file, mode_t mode) int chmod_unix_rfork(char *path, char *name, char *file, mode_t mode) { if (adouble == AD_EA) { -#ifdef HAVE_EAFD +#if defined (HAVE_EAFD) && defined (SOLARIS) sprintf(temp, "runat '%s/%s/%s' chmod 0%o %s", path, name, file, mode, AD_EA_RESO); if (!Quiet) { fprintf(stdout, "%s\n", temp); @@ -302,6 +302,7 @@ int chmod_unix_rfork(char *path, char *name, char *file, mode_t mode) } return 0; #else +#ifndef __APPLE__ sprintf(temp, "%s/%s/._%s", path, name, file); if (!Quiet) { fprintf(stdout, "chmod(%s, 0%o)\n", temp, mode); @@ -313,6 +314,7 @@ int chmod_unix_rfork(char *path, char *name, char *file, mode_t mode) test_failed(); return -1; } +#endif return 0; #endif } else { diff --git a/test/testsuite/ea.h b/test/testsuite/ea.h index b441fc137a..64d2050f22 100644 --- a/test/testsuite/ea.h +++ b/test/testsuite/ea.h @@ -67,7 +67,11 @@ enum { /* Names for our Extended Attributes adouble data */ #define AD_EA_META "org.netatalk.Metadata" +#ifdef __APPLE__ +#define AD_EA_RESO "com.apple.ResourceFork" +#else #define AD_EA_RESO "org.netatalk.ResourceFork" +#endif #define NOT_NETATALK_EA(a) (strcmp((a), AD_EA_META) != 0) && (strcmp((a), AD_EA_RESO) != 0) /**************************************************************************************** diff --git a/test/testsuite/extattr.c b/test/testsuite/extattr.c index 4c11da0a56..b044b93dfd 100644 --- a/test/testsuite/extattr.c +++ b/test/testsuite/extattr.c @@ -79,7 +79,7 @@ static char attr_name[256 +5] = "user."; static const char *prefix(const char *uname) { -#if defined(HAVE_ATTROPEN) +#if defined(HAVE_ATTROPEN) || defined(__APPLE__) return uname; #else strlcpy(attr_name +5, uname, 256);