]> arthur.barton.de Git - netatalk.git/commitdiff
Merge remote-tracking branch 'origin/develop'
authorRalph Boehme <rb@sernet.de>
Sat, 15 Feb 2014 07:22:57 +0000 (08:22 +0100)
committerRalph Boehme <rb@sernet.de>
Sat, 15 Feb 2014 07:22:57 +0000 (08:22 +0100)
18 files changed:
NEWS
VERSION
configure.ac
contrib/macusers/macusers.in
distrib/initscripts/service.systemd.tmpl
doc/manpages/man5/afp.conf.5.xml
etc/afpd/afp_mdns.c
etc/afpd/afp_options.c
etc/afpd/auth.c
etc/afpd/auth.h
etc/afpd/fork.c
etc/cnid_dbd/cmd_dbd_scanvol.c
include/atalk/adouble.h
libatalk/adouble/ad_conv.c
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_open.c
libatalk/compat/misc.c
libatalk/vfs/ea_sys.c

diff --git a/NEWS b/NEWS
index aa61935e63dc160147115074987c9774fad0faa3..9e62be4bf45990a6338f5106a47d6a564b3bb308 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,17 @@
+Changes in 3.1.1
+================
+* FIX: Add asprint() compatibility function for systems lacking it
+* FIX: Fix ressource fork name conversion. Bug #534.
+* FIX: Fix a bug where only the first configured UAM was loaded.
+       Bug #537.
+* UPD: Add support for AFP 3.4. From FR #85.
+* FIX: Registering with mDNS crashed. Bug #540
+* FIX: Saving from applications like Photoshop may fail, because
+       removing the ressource fork AppleDouble file failed. Bug #542.
+* FIX: dbd: remove orphaned ._ AppleDouble files. Bug #549.
+* NEW: afpd: Automatic conversion of ._ AppleDouble files
+       created by OS X. Bug #550.
+
 Changes in 3.1.0
 ================
 * NEW: AFP Spotlight support with Gnome Tracker
@@ -17,6 +31,12 @@ Changes in 3.0.7
 * FIX: Build fixes for the Kerberos UAM
 * UPD: Use dedicated exit code for AFP connections that were dropped
        by the client right after the TCP handshake
+* FIX: Workaround for a problem which cannot be advertized by Avahi. Bug #541.
+* FIX: Registering with mDNS crashed. Bug #540
+* FIX: Saving from applications like Photoshop may fail, because
+       removing the ressource fork AppleDouble file failed. Bug #542.
+* FIX: macusers showed root user. Bug #495.
+* UPD: Add file pathname to logmessage parse_entries: bogus eid. FR#87.
 
 Changes in 3.0.6
 ================
diff --git a/VERSION b/VERSION
index a0cd9f0ccb01eb32efa3e9611582a69d5f2772e8..ec747834113928cc9415cc86835b744e247e3b08 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1.0
\ No newline at end of file
+3.1.1dev
\ No newline at end of file
index 8902ae1e8da15c350c7adaca4064fa5c5dd44e4a..72c060358391743177634a309311575058bcd10c 100644 (file)
@@ -76,7 +76,7 @@ AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
 
 dnl these tests have been comfirmed to be needed in 2011
 AC_CHECK_FUNCS(backtrace_symbols dirfd getusershell pread pwrite pselect)
-AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen mempcpy vasprintf)
+AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen mempcpy vasprintf asprintf)
 AC_CHECK_FUNCS(mmap utime getpagesize) dnl needed by tbd
 
 dnl search for necessary libraries
index b37955512118e7164e9f379ba1d5260e1074dc23..7366a8b6c7135fe4b1fa755eecc3fef381676ff2 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use Socket;
 use File::Basename;
-use vars qw($MAC_PROCESS $PS_STR $MATCH_STR $ASIP_PORT_NO $ASIP_PORT $LSOF);
+use vars qw($MAIN_PID $NETATALK_PROCESS $AFPD_PROCESS $PS_STR $MATCH_STR $ASIP_PORT_NO $ASIP_PORT $LSOF);
 
 # Written for linux; may have to be modified for your brand of Unix.
 # Support for FreeBSD added by Joe Clarke <marcus@marcuscom.com>.
@@ -19,7 +19,8 @@ if ($ARGV[0] =~ /^(-v|-version|--version)$/ ) {
         exit(1);
 }
 
-$MAC_PROCESS = "afpd";
+$NETATALK_PROCESS = "netatalk";
+$AFPD_PROCESS = "afpd";
 if ($^O eq "freebsd" || $^O eq "openbsd") {
         $PS_STR    = "-awwxouser,pid,ppid,start,command";
         $MATCH_STR = '(\w+)\s+(\d+)\s+(\d+)\s+([\d\w:]+)';
@@ -38,10 +39,10 @@ $LSOF = 1;
 my %mac = ();
 
 if ($^O eq "freebsd") {
-        open(SOCKSTAT, "sockstat -4 | grep $MAC_PROCESS | grep -v grep |");
+        open(SOCKSTAT, "sockstat -4 | grep $AFPD_PROCESS | grep -v grep |");
 
         while (<SOCKSTAT>) {
-                next if ($_ !~ /$MAC_PROCESS/);
+                next if ($_ !~ /$AFPD_PROCESS/);
                 $_ =~
                     /\S+\s+\S+\s+(\d+)\s+\d+\s+[\w\d]+\s+[\d\.:]+\s+([\d\.]+)/;
                 my ($pid, $addr, $host);
@@ -82,8 +83,19 @@ if ($^O eq "freebsd") {
 
 open(PS, "ps $PS_STR |") || die "Unable to open a pipe to ``ps''";
 
+$MAIN_PID = 1;
 while (<PS>) {
-        next if ($_ !~ /$MAC_PROCESS/);
+        next if ($_ !~ /$NETATALK_PROCESS/);
+        my ($user, $pid, $ppid, $time, $name, $uid, $t, $ip);
+        $_ =~ /$MATCH_STR/;
+        $MAIN_PID = $2;
+}
+
+close(PS);
+open(PS, "ps $PS_STR |") || die "Unable to open a pipe to ``ps''";
+
+while (<PS>) {
+        next if ($_ !~ /$AFPD_PROCESS/);
         my ($user, $pid, $ppid, $time, $name, $uid, $t, $ip);
         $_ =~ /$MATCH_STR/;
         $user = $1;
@@ -91,7 +103,7 @@ while (<PS>) {
         $ppid = $3;
         $time = $4;
 
-        if ($ppid != 1) {
+        if ($ppid != $MAIN_PID) {
                 if ($^O eq "solaris") {
                         open(PFILES, "pfiles $pid |");
                         while (<PFILES>) {
index 670f1445c8c10c105c9e5ba297ad70c61ea19dec..cd17076a0f1908c5ea5a23ca65890b93e5c51ce4 100644 (file)
@@ -9,7 +9,7 @@ After=syslog.target network.target avahi-daemon.service
 [Service]
 Type=forking
 GuessMainPID=no
-ExecStart=:SBINDIR:/netatalk
+ExecStart=/bin/sh -c :SBINDIR:/netatalk
 PIDFile=:PATH_NETATALK_LOCK:
 ExecReload=/bin/kill -HUP $MAINPID
 Restart=always
index e0c485c5f6e0d0f60908b515847ae7d2f74d49ba..8f398334ca45dbdcef3944e1674f4fd519f5eccc 100644 (file)
@@ -5,7 +5,7 @@
 
     <manvolnum>5</manvolnum>
 
-    <refmiscinfo class="date">13 Sep 2013</refmiscinfo>
+    <refmiscinfo class="date">09 Feb 2013</refmiscinfo>
 
     <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
   </refmeta>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>dbus daemon = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to dbus-daemon binary used by Spotlight feature.
+            The default is <filename>/bin/dbus-daemon</filename>.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>dircachesize = <replaceable>number</replaceable>
           <type>(G)</type></term>
           </listitem>
         </varlistentry>
 
-        <varlistentry>
-          <term>login message = <replaceable>message</replaceable>
-          <type>(G)/(V)</type></term>
-
-          <listitem>
-            <para>Sets a message to be displayed when clients logon to the
-            server. The message should be in <option>unix charset</option> and
-            should be quoted. Extended characters are allowed.</para>
-          </listitem>
-        </varlistentry>
-
         <varlistentry>
           <term>ignored attributes = <replaceable>all | nowrite | nodelete | norename</replaceable>
           <type>(G)/(V)</type></term>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>login message = <replaceable>message</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Sets a message to be displayed when clients logon to the
+            server. The message should be in <option>unix charset</option> and
+            should be quoted. Extended characters are allowed.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>mimic model = <replaceable>model</replaceable>
           <type>(G)</type></term>
           <listitem>
             <para>Whether to enable Spotlight searches. Note: once the global
             option is enabled, any volume that is not enabled won't be
-            searchable at all.</para>
+            searchable at all. See also <emphasis>dbus daemon</emphasis>
+            option.</para>
           </listitem>
         </varlistentry>
 
           <listitem>
             <para>hide files and directories,where the path matches one of the
             '/' delimited vetoed names. The veto string must always be
-            terminated with a '/', eg. "veto1/", "veto1/veto2/".</para>
+            terminated with a '/', eg. "veto files = veto1/", "veto files =
+            veto1/veto2/".</para>
           </listitem>
         </varlistentry>
       </variablelist>
index 2387edad4f542fbe20acef3acf6fb7cdcf9a5152..d3e6cd8403b407d57ac397ad35020613146834bf 100644 (file)
@@ -36,20 +36,58 @@ static pthread_t       poller;
 /*
  * Its easier to use asprintf to set the TXT record values
  */
-#define TXTRecordPrintf(rec, key, args, ...) {           \
-        char *str;                                      \
-        asprintf(&str, args);                           \
-        TXTRecordSetValue(rec, key, strlen(str), str);  \
-        free(str);                                      \
+
+int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... ) 
+{
+    int ret = 0;
+    char *str;
+    va_list ap;
+    va_start( ap, fmt );
+
+    if( 0 > vasprintf(&str, fmt, ap ) ) {
+        va_end(ap);
+        return -1;    
+    }
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
+    }
+
+    free(str);
+    return ret;
+}
+
+int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...) 
+{
+    int ret = 0;
+    char *key = NULL, *str = NULL;
+    va_list ap;
+
+    if( 0 > asprintf(&key, key_fmt, key_var))
+        return -1;
+
+    va_start( ap, fmt );
+    if( 0 > vasprintf(&str, fmt, ap )) {
+        va_end(ap);
+        ret = -1;
+        goto exit;
     }
-#define TXTRecordKeyPrintf(rec, k, var, args, ...) {     \
-        char *key, *str;                                \
-        asprintf(&key, k, var);                         \
-        asprintf(&str, args);                           \
-        TXTRecordSetValue(rec, key, strlen(str), str);  \
-        free(str); free(key);                           \
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
+        goto exit;
     }
 
+exit:
+    if (str)
+        free(str);
+    if (key)
+        free(key);
+    return ret;
+}
+
 static struct pollfd *fds;
 
 /*
@@ -134,7 +172,10 @@ static void register_stuff(const AFPObj *obj) {
 
     /* Register our service, prepare the TXT record */
     TXTRecordCreate(&txt_adisk, 0, NULL);
-    TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
+    if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
+        LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
+        goto fail;
+    }
 
     /* Build AFP volumes list */
     int i = 0;
@@ -150,12 +191,18 @@ static void register_stuff(const AFPObj *obj) {
             if (volume->v_uuid) {
                 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
                     volume->v_localname, volume->v_uuid);
-                TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
-                                   tmpname, volume->v_uuid);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
+                                   tmpname, volume->v_uuid) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
             } else {
                 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
                     volume->v_localname);
-                TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
             }
         }
     }
@@ -170,7 +217,7 @@ static void register_stuff(const AFPObj *obj) {
 
     // Allocate the memory to store our service refs
     svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
-    assert(svc_ref);
+    assert(svc_refs);
     svc_ref_count = 0;
 
     /* AFP server */
@@ -237,7 +284,11 @@ static void register_stuff(const AFPObj *obj) {
             LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
                 dsi->bonjourname, obj->options.mimicmodel);
             TXTRecordCreate(&txt_devinfo, 0, NULL);
-            TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel);
+            if( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
+              LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
+              goto fail;
+            }
+
             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
                                        0,               // no flags
                                        0,               // all network interfaces
index f06bfb373665ca9bc4ff25c8be20501ceb04619e..95faf80807aa078f2b9fc005c8c30a729ec14104 100644 (file)
@@ -176,6 +176,13 @@ static void show_version_extended(void )
        puts( "No" );
 #endif
 
+       printf( "     Spotlight support:\t" );
+#ifdef HAVE_TRACKER
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
        printf( "         DTrace probes:\t" );
 #ifdef WITH_DTRACE
        puts( "Yes" );
index f04404edd2f469af47152fccf96db6ccae7d23e8..8011b56b6fbabe68c0d2f71e935e4f6921637cb1 100644 (file)
@@ -170,6 +170,7 @@ static int set_auth_switch(const AFPObj *obj, int expired)
         afp_switch = postauth_switch;
         switch (obj->afp_version) {
 
+        case 34:
         case 33:
         case 32:
 #ifdef HAVE_ACLS
@@ -228,8 +229,8 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi
         return AFPERR_MAXSESS;
     }
 
-    LOG(log_note, logtype_afpd, "%s Login by %s",
-        afp_versions[afp_version_index].av_name, pwd->pw_name);
+    LOG(log_note, logtype_afpd, "Login by %s (%s)",
+        pwd->pw_name, afp_versions[afp_version_index].av_name);
 
     if (set_groups(obj, pwd) != 0)
         return AFPERR_BADUAM;
@@ -1020,7 +1021,7 @@ int auth_register(const int type, struct uam_obj *uam)
 /* load all of the modules */
 int auth_load(AFPObj *obj, const char *path, const char *list)
 {
-    char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p;
+    char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p, *last;
     struct uam_mod *mod;
     struct stat st;
     size_t len;
@@ -1028,8 +1029,10 @@ int auth_load(AFPObj *obj, const char *path, const char *list)
     if (!path || !*path || !list || (len = strlen(path)) > sizeof(name) - 2)
         return -1;
 
+    LOG(log_debug, logtype_afpd, "auth_load: %s, %s", path, list);
+
     strlcpy(buf, list, sizeof(buf));
-    if ((p = strtok(buf, ", ")) == NULL)
+    if ((p = strtok_r(buf, ", ", &last)) == NULL)
         return -1;
 
     strcpy(name, path);
@@ -1054,7 +1057,7 @@ int auth_load(AFPObj *obj, const char *path, const char *list)
         } else {
             LOG(log_info, logtype_afpd, "uam: uam not found (status=%d)", stat(name, &st));
         }
-        p = strtok(NULL, ", ");
+        p = strtok_r(NULL, ", ", &last);
     }
 
     return 0;
index 2e9d6a584e3f66a94166f9337f49e296d8ac61b0..f53513b6a7f34a4d5b37abd09607d2c9a51e9e2e 100644 (file)
@@ -20,7 +20,8 @@ static const struct afp_versions  afp_versions[] = {
     { "AFPX03", 30 },
     { "AFP3.1", 31 },
     { "AFP3.2", 32 },
-    { "AFP3.3", 33 }
+    { "AFP3.3", 33 },
+    { "AFP3.4", 34 }
 };
 
 /* for GetUserInfo */
index 73abb1e9a102e5a5143303a08011b976cdc982f8..24284c1d745f0bc62fce6668637b7333943a8964 100644 (file)
@@ -500,6 +500,8 @@ openfork_err:
 int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
 {
     struct ofork    *ofork;
+    struct vol      *vol;
+    struct dir      *dir;
     off_t       size;
     uint16_t       ofrefnum, bitmap;
     int                 err;
@@ -522,6 +524,16 @@ int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, s
         return( AFPERR_PARAM );
     }
 
+    vol = ofork->of_vol;
+    if ((dir = dirlookup(vol, ofork->of_did)) == NULL) {
+        LOG(log_error, logtype_afpd, "%s: bad fork did",  of_name(ofork));
+        return AFPERR_MISC;
+    }
+    if (movecwd(vol, dir) != 0) {
+        LOG(log_error, logtype_afpd, "%s: bad fork directory", dir->d_fullpath);
+        return AFPERR_MISC;
+    }
+
     if (ofork->of_vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
index b9fbf3e88ac4d63bea0766caa4fef714b522ffee..ba1cd623e0b54dd778ccf2b5a80cdfc3df0a72d4 100644 (file)
@@ -610,6 +610,22 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
     return db_cnid;
 }
 
+static void check_orphaned(const char *name)
+{
+    int rc;
+    struct stat sb;
+
+    if (strlen(name) < 3)
+        return;
+
+    rc = lstat(&name[2], &sb);
+
+    if (rc != 0 && errno == ENOENT) {
+        dbd_log(LOGSTD, "Removing orphaned AppleDouble \"%s/%s\"", cwdbuf, name);
+        unlink(name);
+    }
+}
+
 /*
   This is called recursively for all dirs.
   volroot=1 means we're in the volume root dir, 0 means we aren't.
@@ -717,6 +733,15 @@ static int dbd_readdir(int volroot, cnid_t did)
            Tests
         **************************************************************************/
 
+        /* Check for invalid names and orphaned ._ files */
+        if (S_ISREG(st.st_mode) && (strncmp(ep->d_name, "._", strlen("._")) == 0))
+            check_orphaned(ep->d_name);
+
+        if (!vol->vfs->vfs_validupath(vol, ep->d_name)) {
+            dbd_log(LOGSTD, "Ignoring \"%s/%s\"", cwdbuf, ep->d_name);
+            continue;
+        }
+
         /* Check for appledouble file, create if missing, but only if we have addir */
         const char *name = NULL;
         adfile_ok = -1;
index 972ce26f7534e40d287d6a4af5d7faaeb2090a98..1881ba028240c0ff5f74edbd162e3d3083e6e5ae 100644 (file)
@@ -376,6 +376,7 @@ struct adouble {
 /* ad_flush.c */
 extern int ad_rebuild_adouble_header_v2(struct adouble *);
 extern int ad_rebuild_adouble_header_ea(struct adouble *);
+extern  int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf);
 extern int ad_copy_header (struct adouble *, struct adouble *);
 extern int ad_flush (struct adouble *);
 extern int ad_close (struct adouble *, int);
index b1785c0163f30cf73eb3dde56794e4b6150e7d0e..2a75ec62b576bb25a6ade1a269cee52db5c98bba 100644 (file)
@@ -210,6 +210,7 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
     static bstring str2f = NULL;
     static bstring strdot = NULL;
     static bstring strcolon = NULL;
+    char *newadpath = NULL;
 
     if (str2e == NULL) {
         str2e = bfromcstr(":2e");
@@ -231,8 +232,13 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
     EC_ZERO( bfindreplace(newpath, str2f, strcolon, 0) );
     
     become_root();
-    if (adflags != ADFLAGS_DIR)
-        rename(vol->ad_path(path, 0), vol->ad_path(bdata(newpath), 0));
+    if (adflags != ADFLAGS_DIR) {
+        if ((newadpath = strdup(vol->ad_path(bdata(newpath), 0))) == NULL) {
+            unbecome_root();
+            EC_FAIL;
+        }
+        rename(vol->ad_path(path, 0), newadpath);
+    }
     rename(path, bdata(newpath));
     unbecome_root();
 
@@ -242,6 +248,8 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
 EC_CLEANUP:
     if (newpath)
         bdestroy(newpath);
+    if (newadpath)
+        free(newadpath);
     EC_EXIT;
 }
 
index 50fd8baef2a2acc7297d0dbb5178490cc456532f..76d44a98051a6820fc3c666491efec9beab499bb 100644 (file)
@@ -146,7 +146,7 @@ int ad_rebuild_adouble_header_ea(struct adouble *ad)
 /*!
  * Prepare adbuf buffer from struct adouble for writing on disk
  */
-static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
+int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
 {
     uint32_t       temp;
     uint16_t       nent;
index 1708d08b18e8200f35d85bb86698d4424b4e15ae..ea221ff4ec62964f827a8e2e8771a945d18c8b27 100644 (file)
@@ -387,13 +387,13 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     return 0;
 }
 
-/* -------------------------------------
-   read in the entries
-*/
-static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
+/**
+ * Read an AppleDouble buffer, returns 0 on success, -1 if an entry was malformatted
+ **/
+static int parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
 {
     uint32_t   eid, len, off;
-    int        warning = 0;
+    int        ret = 0;
 
     /* now, read in the entry bits */
     for (; nentries > 0; nentries-- ) {
@@ -407,17 +407,21 @@ 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)) {
-            ad->ad_eid[ eid ].ade_off = off;
-            ad->ad_eid[ eid ].ade_len = len;
-        } else if (!warning) {
-            warning = 1;
-            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %d", eid);
+        ad->ad_eid[eid].ade_off = off;
+        ad->ad_eid[eid].ade_len = len;
+
+        if (!eid
+            || eid > ADEID_MAX
+            || off >= sizeof(ad->ad_data)
+            || ((eid != ADEID_RFORK) && (off + len >  sizeof(ad->ad_data))))
+        {
+            ret = -1;
+            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
+                (uint)eid, (uint)off, (uint)len);
         }
     }
+
+    return ret;
 }
 
 /* this reads enough of the header so that we can figure out all of
@@ -427,7 +431,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(const char *path _U_, struct adouble *ad, const struct stat *hst)
+static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst)
 {
     char                *buf = ad->ad_data;
     uint16_t            nentries;
@@ -475,7 +479,12 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
     /* figure out all of the entry offsets and lengths. if we aren't
      * able to read a resource fork entry, bail. */
     nentries = len / AD_ENTRY_LEN;
-    parse_entries(ad, buf, nentries);
+    if (parse_entries(ad, buf, nentries) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EIO;
+        return -1;
+    }
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
         ) {
@@ -548,8 +557,83 @@ EC_CLEANUP:
     return 0;
 }
 
+/**
+ * Convert from Apple's ._ file to Netatalk
+ *
+ * Apple's AppleDouble may contain a FinderInfo entry longer then 32 bytes
+ * containing packed xattrs. Netatalk can't deal with that, so we
+ * simply discard the packed xattrs.
+ *
+ * As we call ad_open() which might result in a recursion, just to be sure
+ * use static variable in_conversion to check for that.
+ *
+ * Returns -1 in case an error occured, 0 if no conversion was done, 1 otherwise
+ **/
+static int ad_convert_osx(const char *path, struct adouble *ad)
+{
+    EC_INIT;
+    static bool in_conversion = false;
+    char *map;
+    int finderlen = ad_getentrylen(ad, ADEID_FINDERI);
+    ssize_t origlen;
+
+    if (in_conversion || finderlen == ADEDLEN_FINDERI)
+        return 0;
+    in_conversion = true;
+
+    LOG(log_debug, logtype_ad, "Converting OS X AppleDouble %s, FinderInfo length: %d",
+        fullpathname(path), finderlen);
+
+    origlen = ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK);
+
+    map = mmap(NULL, origlen, PROT_WRITE, MAP_SHARED, ad_reso_fileno(ad), 0);
+    if (map == MAP_FAILED) {
+        LOG(log_error, logtype_ad, "mmap AppleDouble: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+
+    memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
+            map + ad_getentryoff(ad, ADEID_RFORK),
+            ad_getentrylen(ad, ADEID_RFORK));
+
+    ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+    ad->ad_rlen = ad_getentrylen(ad, ADEID_RFORK);
+    ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
+
+    EC_ZERO_LOG( ftruncate(ad_reso_fileno(ad),
+                           ad_getentryoff(ad, ADEID_RFORK)
+                           + ad_getentrylen(ad, ADEID_RFORK)) );
+
+    (void)ad_rebuild_adouble_header_osx(ad, map);
+    munmap(map, origlen);
+
+    /* Create a metadata EA if one doesn't exit */
+    if (strlen(path) < 3)
+        EC_EXIT_STATUS(0);
+    struct adouble adea;
+    ad_init_old(&adea, AD_VERSION_EA, ad->ad_options);
+
+    if (ad_open(&adea, path + 2, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
+        LOG(log_error, logtype_ad, "create metadata: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+    if (adea.ad_mdp->adf_flags & O_CREAT) {
+        memcpy(ad_entry(&adea, ADEID_FINDERI),
+               ad_entry(ad, ADEID_FINDERI),
+               ADEDLEN_FINDERI);
+        ad_flush(&adea);
+    }
+    ad_close(&adea, ADFLAGS_HF);
+
+EC_CLEANUP:
+    in_conversion = false;
+    if (ret != 0)
+        return -1;
+    return 1;
+}
+
 /* 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, const struct stat *hst)
+static int ad_header_read_osx(const char *path, struct adouble *ad, const struct stat *hst)
 {
     EC_INIT;
     struct adouble      adosx;
@@ -558,8 +642,13 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
     int                 len;
     ssize_t             header_len;
     struct stat         st;
+    int                 retry_read = 0;
 
+reread:
+    LOG(log_debug, logtype_ad, "ad_header_read_osx: %s", path ? fullpathname(path) : "");
+    ad_init_old(&adosx, AD_VERSION_EA, ad->ad_options);
     memset(buf, 0, sizeof(adosx.ad_data));
+    adosx.ad_rfp->adf_fd = ad_reso_fileno(ad);
 
     /* read the header */
     EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
@@ -595,7 +684,27 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
     }
 
     nentries = len / AD_ENTRY_LEN;
-    parse_entries(&adosx, buf, nentries);
+    if (parse_entries(&adosx, buf, nentries) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+    }
+
+    if (ad_getentrylen(&adosx, ADEID_FINDERI) != ADEDLEN_FINDERI) {
+        LOG(log_warning, logtype_ad, "Convert OS X to Netatalk AppleDouble: %s",
+            path ? fullpathname(path) : "");
+
+        if (retry_read > 0) {
+            LOG(log_error, logtype_ad, "ad_header_read_osx: %s, giving up", path ? fullpathname(path) : "");
+            errno = EIO;
+            EC_FAIL;
+        }
+        retry_read++;
+        if (ad_convert_osx(path, &adosx) == 1) {
+            goto reread;
+        }
+        errno = EIO;
+        EC_FAIL;
+    }
 
     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
@@ -662,7 +771,12 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     }
 
     /* Now parse entries */
-    parse_entries(ad, buf + AD_HEADER_LEN, nentries);
+    if (parse_entries(ad, buf + AD_HEADER_LEN, nentries)) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EINVAL;
+        EC_FAIL;
+    }
 
     if (nentries != ADEID_NUM_EA
         || !ad_entry(ad, ADEID_FINDERI)
@@ -1352,7 +1466,7 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
         /* Read the adouble header */
         LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
             path, rfpath);
-        EC_NEG1_LOG( ad_header_read_osx(NULL, ad, &st) );
+        EC_NEG1_LOG( ad_header_read_osx(rfpath, ad, &st) );
     }
 #endif
 
@@ -1419,7 +1533,7 @@ off_t ad_getentryoff(const struct adouble *ad, int eid)
 #ifdef HAVE_EAFD
         return 0;
 #else
-        return ADEDOFF_RFORK_OSX;
+        return ad->ad_eid[eid].ade_off;
 #endif
     default:
         return ad->ad_eid[eid].ade_off;
index 32e2943dded16b60795979e39b956725d5103349..614c6f2d155ab6b04b2184262b7bea2a7b622649 100644 (file)
@@ -61,3 +61,17 @@ int vasprintf(char **ret, const char *fmt, va_list ap)
     }
 }
 #endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...)
+{
+    va_list ap;
+    int len;
+
+    va_start(ap, fmt);
+    len = vasprintf(strp, fmt, ap);
+    va_end(ap);
+
+    return len;
+}
+#endif
index 374248bc8a9e4696eedf28519aeeec5a80fa7761..0b899903ee433a0a70f50a9ffb0cab0cd96df406 100644 (file)
@@ -86,6 +86,8 @@ int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
 
         case ENOATTR:
         case ENOENT:
+            if (vol->v_obj->afp_version >= 34)
+                return AFPERR_NOITEM;
             return AFPERR_MISC;
 
         default:
@@ -161,6 +163,8 @@ int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
             return AFPERR_MISC;
 
         case ENOATTR:
+            if (vol->v_obj->afp_version >= 34)
+                return AFPERR_NOITEM;
             return AFPERR_MISC;
 
         default:
@@ -313,8 +317,13 @@ int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
                 getcwdpath(), uname, attruname);
             return AFPERR_EXIST;
+        case ENOATTR:
+        case ENOENT:
+            if ((attr_flag & XATTR_REPLACE) && (vol->v_obj->afp_version >= 34))
+                return AFPERR_NOITEM;
+            return AFPERR_MISC;
         default:
-            LOG(log_error, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
+            LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
                 getcwdpath(), uname, attruname, attrsize, 
                 oflag & O_CREAT ? "XATTR_CREATE" : "-",
                 oflag & O_TRUNC ? "XATTR_REPLACE" : "-",