+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
* 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
================
-3.1.0
\ No newline at end of file
+3.1.1dev
\ No newline at end of file
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
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>.
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:]+)';
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);
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;
$ppid = $3;
$time = $4;
- if ($ppid != 1) {
+ if ($ppid != $MAIN_PID) {
if ($^O eq "solaris") {
open(PFILES, "pfiles $pid |");
while (<PFILES>) {
[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
<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>
/*
* 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;
/*
/* 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;
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;
+ }
}
}
}
// 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 */
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
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" );
afp_switch = postauth_switch;
switch (obj->afp_version) {
+ case 34:
case 33:
case 32:
#ifdef HAVE_ACLS
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;
/* 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;
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);
} 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;
{ "AFPX03", 30 },
{ "AFP3.1", 31 },
{ "AFP3.2", 32 },
- { "AFP3.3", 33 }
+ { "AFP3.3", 33 },
+ { "AFP3.4", 34 }
};
/* for GetUserInfo */
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;
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;
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.
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;
/* 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);
static bstring str2f = NULL;
static bstring strdot = NULL;
static bstring strcolon = NULL;
+ char *newadpath = NULL;
if (str2e == NULL) {
str2e = bfromcstr(":2e");
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();
EC_CLEANUP:
if (newpath)
bdestroy(newpath);
+ if (newadpath)
+ free(newadpath);
EC_EXIT;
}
/*!
* 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;
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-- ) {
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
* 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;
/* 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))
) {
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;
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) );
}
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)
}
/* 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)
/* 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
#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;
}
}
#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
case ENOATTR:
case ENOENT:
+ if (vol->v_obj->afp_version >= 34)
+ return AFPERR_NOITEM;
return AFPERR_MISC;
default:
return AFPERR_MISC;
case ENOATTR:
+ if (vol->v_obj->afp_version >= 34)
+ return AFPERR_NOITEM;
return AFPERR_MISC;
default:
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" : "-",