Skip to content

Commit

Permalink
Add native metadata storage for macOS hosts.
Browse files Browse the repository at this point in the history
-Store all extended attributes without the `user.` prefix on macOS. This will orphan Netatalk metadata created on past versions, but is needed for native extended attributes to work properly on shares. Unlike Linux, macOS doesn't prefix user created extended attributes with `user.`.

-Store resource forks natively by using the `/..namedfork/rsrc` path instead of creating AppleDouble files. No need for separate files since the file system supports it natively.

-Synchronize native FinderInfo (stored in `com.apple.FinderInfo`) with Netatalk. Any changes made to FinderInfo by Netatalk clients or by the host OS are now visible to each other. Note that the contents of `com.apple.FinderInfo` take priority of that over what is stored in Netatalk's extended attributes.

-Fix testsuite for native resource fork handling.
  • Loading branch information
NJRoadfan committed Dec 15, 2024
1 parent 1cd4f95 commit 07f87de
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 11 deletions.
5 changes: 5 additions & 0 deletions include/atalk/ea.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ 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"
#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)

/****************************************************************************************
Expand Down
18 changes: 17 additions & 1 deletion libatalk/adouble/ad_flush.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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;
Expand Down
32 changes: 28 additions & 4 deletions libatalk/adouble/ad_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion libatalk/vfs/extattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 14 additions & 2 deletions libatalk/vfs/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 4 additions & 2 deletions test/testsuite/adoublehelper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions test/testsuite/ea.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

/****************************************************************************************
Expand Down
2 changes: 1 addition & 1 deletion test/testsuite/extattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 07f87de

Please sign in to comment.