summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2019-07-09 13:10:00 +0200
committerJeremy Allison <jra@samba.org>2019-07-12 21:31:29 +0000
commitdbc840e740a447b3e7771bbe0c607a4623518e98 (patch)
tree4a9ce7c7b40a143a65b217cd526bd931639b0d13 /source3
parentf9df2b67ced95bfad4bdac925dc200ca7daf111e (diff)
downloadsamba-dbc840e740a447b3e7771bbe0c607a4623518e98.tar.gz
vfs_fruit: move AppleDouble functionality to a dedicated subsystem
It's still tied to the VFS, therefor it lives in source3/modules/. Reduces code bloat in vfs_fruit and makes it possible to use it from other parts of the codebase in future. Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3')
-rw-r--r--source3/lib/adouble.c1884
-rw-r--r--source3/lib/adouble.h182
-rw-r--r--source3/modules/vfs_fruit.c2009
-rw-r--r--source3/modules/wscript_build2
-rw-r--r--source3/wscript_build4
5 files changed, 2072 insertions, 2009 deletions
diff --git a/source3/lib/adouble.c b/source3/lib/adouble.c
new file mode 100644
index 00000000000..78ef187335a
--- /dev/null
+++ b/source3/lib/adouble.c
@@ -0,0 +1,1884 @@
+/*
+ * Samba AppleDouble helpers
+ *
+ * Copyright (C) Ralph Boehme, 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "adouble.h"
+#include "MacExtensions.h"
+#include "string_replace.h"
+#include "smbd/smbd.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+
+/* Number of actually used entries */
+#define ADEID_NUM_XATTR 8
+#define ADEID_NUM_DOT_UND 2
+#define ADEID_NUM_RSRC_XATTR 1
+
+/* Sizes of relevant entry bits */
+#define ADEDLEN_MAGIC 4
+#define ADEDLEN_VERSION 4
+#define ADEDLEN_FILLER 16
+#define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
+#define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
+#define ADEDLEN_NENTRIES 2
+#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
+ ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
+#define AD_ENTRY_LEN_EID 4
+#define AD_ENTRY_LEN_OFF 4
+#define AD_ENTRY_LEN_LEN 4
+#define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
+
+/* Offsets */
+#define ADEDOFF_MAGIC 0
+#define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
+#define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
+#define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
+
+#define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
+ (ADEID_NUM_XATTR * AD_ENTRY_LEN))
+#define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
+#define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
+#define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
+ ADEDLEN_FILEDATESI)
+#define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
+#define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
+#define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
+#define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
+
+#define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
+ (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
+#define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
+
+#define AD_DATASZ_XATTR (AD_HEADER_LEN + \
+ (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
+ ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
+ ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
+ ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
+ ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
+
+#if AD_DATASZ_XATTR != 402
+#error bad size for AD_DATASZ_XATTR
+#endif
+
+#define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
+ (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
+ ADEDLEN_FINDERI)
+#if AD_DATASZ_DOT_UND != 82
+#error bad size for AD_DATASZ_DOT_UND
+#endif
+
+#define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
+#define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
+#define AD_XATTR_HDR_SIZE 36
+#define AD_XATTR_MAX_HDR_SIZE 65536
+
+/*
+ * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
+ * representation as well as the on-disk format.
+ *
+ * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
+ * the length of the FinderInfo entry is larger then 32 bytes. It is then
+ * preceeded with 2 bytes padding.
+ *
+ * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
+ */
+
+struct ad_xattr_header {
+ uint32_t adx_magic; /* ATTR_HDR_MAGIC */
+ uint32_t adx_debug_tag; /* for debugging == file id of owning file */
+ uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
+ uint32_t adx_data_start; /* file offset to attribute data area */
+ uint32_t adx_data_length; /* length of attribute data area */
+ uint32_t adx_reserved[3];
+ uint16_t adx_flags;
+ uint16_t adx_num_attrs;
+};
+
+/* On-disk entries are aligned on 4 byte boundaries */
+struct ad_xattr_entry {
+ uint32_t adx_offset; /* file offset to data */
+ uint32_t adx_length; /* size of attribute data */
+ uint16_t adx_flags;
+ uint8_t adx_namelen; /* included the NULL terminator */
+ char *adx_name; /* NULL-terminated UTF-8 name */
+};
+
+struct ad_entry {
+ size_t ade_off;
+ size_t ade_len;
+};
+
+struct adouble {
+ files_struct *ad_fsp;
+ bool ad_opened;
+ adouble_type_t ad_type;
+ uint32_t ad_magic;
+ uint32_t ad_version;
+ uint8_t ad_filler[ADEDLEN_FILLER];
+ struct ad_entry ad_eid[ADEID_MAX];
+ char *ad_data;
+ struct ad_xattr_header adx_header;
+ struct ad_xattr_entry *adx_entries;
+};
+
+struct ad_entry_order {
+ uint32_t id, offset, len;
+};
+
+/* Netatalk AppleDouble metadata xattr */
+static const
+struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
+ {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
+ {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
+ {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
+ {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
+ {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
+ {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
+ {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
+ {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
+ {0, 0, 0}
+};
+
+/* AppleDouble resource fork file (the ones prefixed by "._") */
+static const
+struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
+ {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
+ {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
+ {0, 0, 0}
+};
+
+/* Conversion from enumerated id to on-disk AppleDouble id */
+#define AD_EID_DISK(a) (set_eid[a])
+static const uint32_t set_eid[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ AD_DEV, AD_INO, AD_SYN, AD_ID
+};
+
+static char empty_resourcefork[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
+ 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
+ 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
+ 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
+ 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
+ 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
+};
+
+size_t ad_getentrylen(const struct adouble *ad, int eid)
+{
+ return ad->ad_eid[eid].ade_len;
+}
+
+size_t ad_getentryoff(const struct adouble *ad, int eid)
+{
+ return ad->ad_eid[eid].ade_off;
+}
+
+size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
+{
+ return ad->ad_eid[eid].ade_len = len;
+}
+
+size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
+{
+ return ad->ad_eid[eid].ade_off = off;
+}
+
+/**
+ * Return a pointer to an AppleDouble entry
+ *
+ * Returns NULL if the entry is not present
+ **/
+char *ad_get_entry(const struct adouble *ad, int eid)
+{
+ off_t off = ad_getentryoff(ad, eid);
+ size_t len = ad_getentrylen(ad, eid);
+
+ if (off == 0 || len == 0) {
+ return NULL;
+ }
+
+ return ad->ad_data + off;
+}
+
+/**
+ * Get a date
+ **/
+int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
+{
+ bool xlate = (dateoff & AD_DATE_UNIX);
+ char *p = NULL;
+
+ dateoff &= AD_DATE_MASK;
+ p = ad_get_entry(ad, ADEID_FILEDATESI);
+ if (p == NULL) {
+ return -1;
+ }
+
+ if (dateoff > AD_DATE_ACCESS) {
+ return -1;
+ }
+
+ memcpy(date, p + dateoff, sizeof(uint32_t));
+
+ if (xlate) {
+ *date = AD_DATE_TO_UNIX(*date);
+ }
+ return 0;
+}
+
+/**
+ * Set a date
+ **/
+int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
+{
+ bool xlate = (dateoff & AD_DATE_UNIX);
+ char *p = NULL;
+
+ p = ad_get_entry(ad, ADEID_FILEDATESI);
+ if (p == NULL) {
+ return -1;
+ }
+
+ dateoff &= AD_DATE_MASK;
+ if (xlate) {
+ date = AD_DATE_FROM_UNIX(date);
+ }
+
+ if (dateoff > AD_DATE_ACCESS) {
+ return -1;
+ }
+
+ memcpy(p + dateoff, &date, sizeof(date));
+
+ return 0;
+}
+
+
+/**
+ * Map on-disk AppleDouble id to enumerated id
+ **/
+static uint32_t get_eid(uint32_t eid)
+{
+ if (eid <= 15) {
+ return eid;
+ }
+
+ switch (eid) {
+ case AD_DEV:
+ return ADEID_PRIVDEV;
+ case AD_INO:
+ return ADEID_PRIVINO;
+ case AD_SYN:
+ return ADEID_PRIVSYN;
+ case AD_ID:
+ return ADEID_PRIVID;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Pack AppleDouble structure into data buffer
+ **/
+static bool ad_pack(struct adouble *ad)
+{
+ uint32_t eid;
+ uint16_t nent;
+ uint32_t bufsize;
+ uint32_t offset = 0;
+
+ bufsize = talloc_get_size(ad->ad_data);
+ if (bufsize < AD_DATASZ_DOT_UND) {
+ DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
+ return false;
+ }
+
+ if (offset + ADEDLEN_MAGIC < offset ||
+ offset + ADEDLEN_MAGIC >= bufsize) {
+ return false;
+ }
+ RSIVAL(ad->ad_data, offset, ad->ad_magic);
+ offset += ADEDLEN_MAGIC;
+
+ if (offset + ADEDLEN_VERSION < offset ||
+ offset + ADEDLEN_VERSION >= bufsize) {
+ return false;
+ }
+ RSIVAL(ad->ad_data, offset, ad->ad_version);
+ offset += ADEDLEN_VERSION;
+
+ if (offset + ADEDLEN_FILLER < offset ||
+ offset + ADEDLEN_FILLER >= bufsize) {
+ return false;
+ }
+ if (ad->ad_type == ADOUBLE_RSRC) {
+ memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
+ }
+ offset += ADEDLEN_FILLER;
+
+ if (offset + ADEDLEN_NENTRIES < offset ||
+ offset + ADEDLEN_NENTRIES >= bufsize) {
+ return false;
+ }
+ offset += ADEDLEN_NENTRIES;
+
+ for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
+ if (ad->ad_eid[eid].ade_off == 0) {
+ /*
+ * ade_off is also used as indicator whether a
+ * specific entry is used or not
+ */
+ continue;
+ }
+
+ if (offset + AD_ENTRY_LEN_EID < offset ||
+ offset + AD_ENTRY_LEN_EID >= bufsize) {
+ return false;
+ }
+ RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
+ offset += AD_ENTRY_LEN_EID;
+
+ if (offset + AD_ENTRY_LEN_OFF < offset ||
+ offset + AD_ENTRY_LEN_OFF >= bufsize) {
+ return false;
+ }
+ RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
+ offset += AD_ENTRY_LEN_OFF;
+
+ if (offset + AD_ENTRY_LEN_LEN < offset ||
+ offset + AD_ENTRY_LEN_LEN >= bufsize) {
+ return false;
+ }
+ RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
+ offset += AD_ENTRY_LEN_LEN;
+
+ nent++;
+ }
+
+ if (ADEDOFF_NENTRIES + 2 >= bufsize) {
+ return false;
+ }
+ RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
+
+ return true;
+}
+
+static bool ad_unpack_xattrs(struct adouble *ad)
+{
+ struct ad_xattr_header *h = &ad->adx_header;
+ const char *p = ad->ad_data;
+ uint32_t hoff;
+ uint32_t i;
+
+ if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
+ return true;
+ }
+
+ /* 2 bytes padding */
+ hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
+
+ h->adx_magic = RIVAL(p, hoff + 0);
+ h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
+ h->adx_total_size = RIVAL(p, hoff + 8);
+ h->adx_data_start = RIVAL(p, hoff + 12);
+ h->adx_data_length = RIVAL(p, hoff + 16);
+ h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
+ h->adx_num_attrs = RSVAL(p, hoff + 34);
+
+ if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
+ DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
+ return false;
+ }
+
+ if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
+ DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
+ return false;
+ }
+ if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
+ DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
+ return false;
+ }
+
+ if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
+ DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
+ return false;
+ }
+
+ if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
+ DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
+ return false;
+ }
+ if ((h->adx_data_start + h->adx_data_length) >
+ ad->adx_header.adx_total_size)
+ {
+ DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
+ return false;
+ }
+
+ if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
+ DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
+ return false;
+ }
+
+ if (h->adx_num_attrs == 0) {
+ return true;
+ }
+
+ ad->adx_entries = talloc_zero_array(
+ ad, struct ad_xattr_entry, h->adx_num_attrs);
+ if (ad->adx_entries == NULL) {
+ return false;
+ }
+
+ hoff += AD_XATTR_HDR_SIZE;
+
+ for (i = 0; i < h->adx_num_attrs; i++) {
+ struct ad_xattr_entry *e = &ad->adx_entries[i];
+
+ hoff = (hoff + 3) & ~3;
+
+ e->adx_offset = RIVAL(p, hoff + 0);
+ e->adx_length = RIVAL(p, hoff + 4);
+ e->adx_flags = RSVAL(p, hoff + 8);
+ e->adx_namelen = *(p + hoff + 10);
+
+ if (e->adx_offset >= ad->adx_header.adx_total_size) {
+ DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
+ e->adx_offset);
+ return false;
+ }
+
+ if ((e->adx_offset + e->adx_length) < e->adx_offset) {
+ DBG_ERR("Bad adx_length: %" PRIx32 "\n",
+ e->adx_length);
+ return false;
+ }
+
+ if ((e->adx_offset + e->adx_length) >
+ ad->adx_header.adx_total_size)
+ {
+ DBG_ERR("Bad adx_length: %" PRIx32 "\n",
+ e->adx_length);
+ return false;
+ }
+
+ if (e->adx_namelen == 0) {
+ DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+ e->adx_namelen);
+ return false;
+ }
+ if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
+ DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+ e->adx_namelen);
+ return false;
+ }
+ if ((hoff + 11 + e->adx_namelen) >
+ ad->adx_header.adx_data_start)
+ {
+ DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+ e->adx_namelen);
+ return false;
+ }
+
+ e->adx_name = talloc_strndup(ad->adx_entries,
+ p + hoff + 11,
+ e->adx_namelen);
+ if (e->adx_name == NULL) {
+ return false;
+ }
+
+ DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
+ e->adx_name, e->adx_offset, e->adx_length);
+ dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
+ e->adx_length);
+
+ hoff += 11 + e->adx_namelen;
+ }
+
+ return true;
+}
+
+/**
+ * Unpack an AppleDouble blob into a struct adoble
+ **/
+static bool ad_unpack(struct adouble *ad, const size_t nentries,
+ size_t filesize)
+{
+ size_t bufsize = talloc_get_size(ad->ad_data);
+ size_t adentries, i;
+ uint32_t eid, len, off;
+ bool ok;
+
+ /*
+ * The size of the buffer ad->ad_data is checked when read, so
+ * we wouldn't have to check our own offsets, a few extra
+ * checks won't hurt though. We have to check the offsets we
+ * read from the buffer anyway.
+ */
+
+ if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
+ DEBUG(1, ("bad size\n"));
+ return false;
+ }
+
+ ad->ad_magic = RIVAL(ad->ad_data, 0);
+ ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
+ if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
+ DEBUG(1, ("wrong magic or version\n"));
+ return false;
+ }
+
+ memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
+
+ adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
+ if (adentries != nentries) {
+ DEBUG(1, ("invalid number of entries: %zu\n",
+ adentries));
+ return false;
+ }
+
+ /* now, read in the entry bits */
+ for (i = 0; i < adentries; i++) {
+ eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
+ eid = get_eid(eid);
+ off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
+ len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
+
+ if (!eid || eid >= ADEID_MAX) {
+ DEBUG(1, ("bogus eid %d\n", eid));
+ return false;
+ }
+
+ /*
+ * All entries other than the resource fork are
+ * expected to be read into the ad_data buffer, so
+ * ensure the specified offset is within that bound
+ */
+ if ((off > bufsize) && (eid != ADEID_RFORK)) {
+ DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+ eid, off, len));
+ return false;
+ }
+
+ /*
+ * All entries besides FinderInfo and resource fork
+ * must fit into the buffer. FinderInfo is special as
+ * it may be larger then the default 32 bytes (if it
+ * contains marshalled xattrs), but we will fixup that
+ * in ad_convert(). And the resource fork is never
+ * accessed directly by the ad_data buf (also see
+ * comment above) anyway.
+ */
+ if ((eid != ADEID_RFORK) &&
+ (eid != ADEID_FINDERI) &&
+ ((off + len) > bufsize)) {
+ DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+ eid, off, len));
+ return false;
+ }
+
+ /*
+ * That would be obviously broken
+ */
+ if (off > filesize) {
+ DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+ eid, off, len));
+ return false;
+ }
+
+ /*
+ * Check for any entry that has its end beyond the
+ * filesize.
+ */
+ if (off + len < off) {
+ DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
+ ", len: %" PRIu32 "\n",
+ eid, off, len));
+ return false;
+
+ }
+ if (off + len > filesize) {
+ /*
+ * If this is the resource fork entry, we fix
+ * up the length, for any other entry we bail
+ * out.
+ */
+ if (eid != ADEID_RFORK) {
+ DEBUG(1, ("bogus eid %d: off: %" PRIu32
+ ", len: %" PRIu32 "\n",
+ eid, off, len));
+ return false;
+ }
+
+ /*
+ * Fixup the resource fork entry by limiting
+ * the size to entryoffset - filesize.
+ */
+ len = filesize - off;
+ DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
+ ", len: %" PRIu32 "\n", off, len));
+ }
+
+ ad->ad_eid[eid].ade_off = off;
+ ad->ad_eid[eid].ade_len = len;
+ }
+
+ ok = ad_unpack_xattrs(ad);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ad_convert_move_reso(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ char *buf = NULL;
+ size_t rforklen;
+ size_t rforkoff;
+ ssize_t n;
+ int ret;
+ bool ok;
+
+ rforklen = ad_getentrylen(ad, ADEID_RFORK);
+ if (rforklen == 0) {
+ return true;
+ }
+
+ buf = talloc_size(ad, rforklen);
+ if (buf == NULL) {
+ /*
+ * This allocates a buffer for reading the resource fork data in
+ * one big swoop. Resource forks won't be larger then, say, 64
+ * MB, I swear, so just doing the allocation with the talloc
+ * limit as safeguard seems safe.
+ */
+ DBG_ERR("Failed to allocate %zu bytes for rfork\n",
+ rforklen);
+ return false;
+ }
+
+ rforkoff = ad_getentryoff(ad, ADEID_RFORK);
+
+ n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
+ if (n != rforklen) {
+ DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
+ rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+ return false;
+ }
+
+ rforkoff = ADEDOFF_RFORK_DOT_UND;
+
+ n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
+ if (n != rforklen) {
+ DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
+ rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+ return false;
+ }
+
+ ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
+ ok = ad_pack(ad);
+ if (!ok) {
+ DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+ return false;
+ }
+
+ ret = ad_fset(handle, ad, ad->ad_fsp);
+ if (ret != 0) {
+ DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
+ return false;
+ }
+
+ return true;
+}
+
+static bool ad_convert_xattr(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname,
+ const char *catia_mappings,
+ bool *converted_xattr)
+{
+ static struct char_mappings **string_replace_cmaps = NULL;
+ uint16_t i;
+ int saved_errno = 0;
+ NTSTATUS status;
+ int rc;
+ bool ok;
+
+ *converted_xattr = false;
+
+ if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
+ return true;
+ }
+
+ if (string_replace_cmaps == NULL) {
+ const char **mappings = NULL;
+
+ mappings = str_list_make_v3_const(
+ talloc_tos(), catia_mappings, NULL);
+ if (mappings == NULL) {
+ return false;
+ }
+ string_replace_cmaps = string_replace_init_map(mappings);
+ TALLOC_FREE(mappings);
+ }
+
+ for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
+ struct ad_xattr_entry *e = &ad->adx_entries[i];
+ char *mapped_name = NULL;
+ char *tmp = NULL;
+ struct smb_filename *stream_name = NULL;
+ files_struct *fsp = NULL;
+ ssize_t nwritten;
+
+ status = string_replace_allocate(handle->conn,
+ e->adx_name,
+ string_replace_cmaps,
+ talloc_tos(),
+ &mapped_name,
+ vfs_translate_to_windows);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
+ {
+ DBG_ERR("string_replace_allocate failed\n");
+ ok = false;
+ goto fail;
+ }
+
+ tmp = mapped_name;
+ mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
+ TALLOC_FREE(tmp);
+ if (mapped_name == NULL) {
+ ok = false;
+ goto fail;
+ }
+
+ stream_name = synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ mapped_name,
+ NULL,
+ smb_fname->flags);
+ TALLOC_FREE(mapped_name);
+ if (stream_name == NULL) {
+ DBG_ERR("synthetic_smb_fname failed\n");
+ ok = false;
+ goto fail;
+ }
+
+ DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
+
+ status = SMB_VFS_CREATE_FILE(
+ handle->conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ stream_name, /* fname */
+ FILE_GENERIC_WRITE, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN_IF, /* create_disposition */
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* psbuf */
+ NULL, NULL); /* create context */
+ TALLOC_FREE(stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+ ok = false;
+ goto fail;
+ }
+
+ nwritten = SMB_VFS_PWRITE(fsp,
+ ad->ad_data + e->adx_offset,
+ e->adx_length,
+ 0);
+ if (nwritten == -1) {
+ DBG_ERR("SMB_VFS_PWRITE failed\n");
+ saved_errno = errno;
+ close_file(NULL, fsp, ERROR_CLOSE);
+ errno = saved_errno;
+ ok = false;
+ goto fail;
+ }
+
+ status = close_file(NULL, fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ ok = false;
+ goto fail;
+ }
+ fsp = NULL;
+ }
+
+ ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+ goto fail;
+ }
+
+ rc = ad_fset(handle, ad, ad->ad_fsp);
+ if (rc != 0) {
+ DBG_ERR("ad_fset on [%s] failed: %s\n",
+ fsp_str_dbg(ad->ad_fsp), strerror(errno));
+ ok = false;
+ goto fail;
+ }
+
+ ok = ad_convert_move_reso(handle, ad, smb_fname);
+ if (!ok) {
+ goto fail;
+ }
+
+ *converted_xattr = true;
+ ok = true;
+
+fail:
+ return ok;
+}
+
+static bool ad_convert_finderinfo(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ char *p_ad = NULL;
+ AfpInfo *ai = NULL;
+ DATA_BLOB aiblob;
+ struct smb_filename *stream_name = NULL;
+ files_struct *fsp = NULL;
+ size_t size;
+ ssize_t nwritten;
+ NTSTATUS status;
+ int saved_errno = 0;
+ int cmp;
+
+ cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
+ if (cmp != 0) {
+ return true;
+ }
+
+ p_ad = ad_get_entry(ad, ADEID_FINDERI);
+ if (p_ad == NULL) {
+ return false;
+ }
+
+ ai = afpinfo_new(talloc_tos());
+ if (ai == NULL) {
+ return false;
+ }
+
+ memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
+
+ aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
+ if (aiblob.data == NULL) {
+ TALLOC_FREE(ai);
+ return false;
+ }
+
+ size = afpinfo_pack(ai, (char *)aiblob.data);
+ TALLOC_FREE(ai);
+ if (size != AFP_INFO_SIZE) {
+ return false;
+ }
+
+ stream_name = synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ AFPINFO_STREAM,
+ NULL,
+ smb_fname->flags);
+ if (stream_name == NULL) {
+ data_blob_free(&aiblob);
+ DBG_ERR("synthetic_smb_fname failed\n");
+ return false;
+ }
+
+ DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
+
+ status = SMB_VFS_CREATE_FILE(
+ handle->conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ stream_name, /* fname */
+ FILE_GENERIC_WRITE, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN_IF, /* create_disposition */
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* psbuf */
+ NULL, NULL); /* create context */
+ TALLOC_FREE(stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+ return false;
+ }
+
+ nwritten = SMB_VFS_PWRITE(fsp,
+ aiblob.data,
+ aiblob.length,
+ 0);
+ if (nwritten == -1) {
+ DBG_ERR("SMB_VFS_PWRITE failed\n");
+ saved_errno = errno;
+ close_file(NULL, fsp, ERROR_CLOSE);
+ errno = saved_errno;
+ return false;
+ }
+
+ status = close_file(NULL, fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ fsp = NULL;
+
+ return true;
+}
+
+static bool ad_convert_truncate(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ int rc;
+ off_t newlen;
+
+ newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
+
+ rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
+ if (rc != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
+ struct adouble *ad,
+ uint32_t flags,
+ bool *blank)
+{
+ size_t rforklen = sizeof(empty_resourcefork);
+ char buf[rforklen];
+ ssize_t nread;
+ int cmp;
+ int rc;
+ bool ok;
+
+ *blank = false;
+
+ if (!(flags & AD_CONV_WIPE_BLANK)) {
+ return true;
+ }
+
+ if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
+ return true;
+ }
+
+ nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
+ if (nread != rforklen) {
+ DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
+ rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+ return false;
+ }
+
+ cmp = memcmp(buf, empty_resourcefork, rforklen);
+ if (cmp != 0) {
+ return true;
+ }
+
+ ad_setentrylen(ad, ADEID_RFORK, 0);
+ ok = ad_pack(ad);
+ if (!ok) {
+ return false;
+ }
+
+ rc = ad_fset(handle, ad, ad->ad_fsp);
+ if (rc != 0) {
+ DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
+ return false;
+ }
+
+ *blank = true;
+ return true;
+}
+
+static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname,
+ uint32_t flags)
+{
+ struct smb_filename *ad_name = NULL;
+ int rc;
+
+ if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
+ return true;
+ }
+
+ if (!(flags & AD_CONV_DELETE)) {
+ return true;
+ }
+
+ rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
+ if (rc != 0) {
+ return false;
+ }
+
+ rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
+ if (rc != 0) {
+ DBG_ERR("Unlinking [%s] failed: %s\n",
+ smb_fname_str_dbg(ad_name), strerror(errno));
+ TALLOC_FREE(ad_name);
+ return false;
+ }
+
+ DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
+ TALLOC_FREE(ad_name);
+
+ return true;
+}
+
+/**
+ * Convert from Apple's ._ file to Netatalk
+ *
+ * Apple's AppleDouble may contain a FinderInfo entry longer then 32
+ * bytes containing packed xattrs.
+ *
+ * @return -1 in case an error occurred, 0 if no conversion was done, 1
+ * otherwise
+ **/
+int ad_convert(struct vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ const char *catia_mappings,
+ uint32_t flags)
+{
+ struct adouble *ad = NULL;
+ bool ok;
+ bool converted_xattr = false;
+ bool blank;
+ int ret;
+
+ ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
+ if (ad == NULL) {
+ return 0;
+ }
+
+ ok = ad_convert_xattr(handle,
+ ad,
+ smb_fname,
+ catia_mappings,
+ &converted_xattr);
+ if (!ok) {
+ ret = -1;
+ goto done;
+ }
+
+ ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
+ if (!ok) {
+ ret = -1;
+ goto done;
+ }
+
+ if (converted_xattr || blank) {
+ ok = ad_convert_truncate(handle, ad, smb_fname);
+ if (!ok) {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ ok = ad_convert_finderinfo(handle, ad, smb_fname);
+ if (!ok) {
+ DBG_ERR("Failed to convert [%s]\n",
+ smb_fname_str_dbg(smb_fname));
+ ret = -1;
+ goto done;
+ }
+
+ ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
+ if (!ok) {
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ TALLOC_FREE(ad);
+ return ret;
+}
+
+/**
+ * Read and parse Netatalk AppleDouble metadata xattr
+ **/
+static ssize_t ad_read_meta(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ int rc = 0;
+ ssize_t ealen;
+ bool ok;
+
+ DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
+
+ ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
+ AFPINFO_EA_NETATALK, ad->ad_data,
+ AD_DATASZ_XATTR);
+ if (ealen == -1) {
+ switch (errno) {
+ case ENOATTR:
+ case ENOENT:
+ if (errno == ENOATTR) {
+ errno = ENOENT;
+ }
+ rc = -1;
+ goto exit;
+ default:
+ DEBUG(2, ("error reading meta xattr: %s\n",
+ strerror(errno)));
+ rc = -1;
+ goto exit;
+ }
+ }
+ if (ealen != AD_DATASZ_XATTR) {
+ DEBUG(2, ("bad size %zd\n", ealen));
+ errno = EINVAL;
+ rc = -1;
+ goto exit;
+ }
+
+ /* Now parse entries */
+ ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
+ if (!ok) {
+ DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
+ errno = EINVAL;
+ rc = -1;
+ goto exit;
+ }
+
+ if (!ad_getentryoff(ad, ADEID_FINDERI)
+ || !ad_getentryoff(ad, ADEID_COMMENT)
+ || !ad_getentryoff(ad, ADEID_FILEDATESI)
+ || !ad_getentryoff(ad, ADEID_AFPFILEI)
+ || !ad_getentryoff(ad, ADEID_PRIVDEV)
+ || !ad_getentryoff(ad, ADEID_PRIVINO)
+ || !ad_getentryoff(ad, ADEID_PRIVSYN)
+ || !ad_getentryoff(ad, ADEID_PRIVID)) {
+ DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
+ errno = EINVAL;
+ rc = -1;
+ goto exit;
+ }
+
+exit:
+ DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
+ smb_fname->base_name, rc));
+
+ if (rc != 0) {
+ ealen = -1;
+ if (errno == EINVAL) {
+ become_root();
+ (void)SMB_VFS_REMOVEXATTR(handle->conn,
+ smb_fname,
+ AFPINFO_EA_NETATALK);
+ unbecome_root();
+ errno = ENOENT;
+ }
+ }
+ return ealen;
+}
+
+static int ad_open_rsrc(vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ int flags,
+ mode_t mode,
+ files_struct **_fsp)
+{
+ int ret;
+ struct smb_filename *adp_smb_fname = NULL;
+ files_struct *fsp = NULL;
+ uint32_t access_mask;
+ uint32_t share_access;
+ uint32_t create_disposition;
+ NTSTATUS status;
+
+ ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
+ if (ret != 0) {
+ return -1;
+ }
+
+ ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
+ if (ret != 0) {
+ TALLOC_FREE(adp_smb_fname);
+ return -1;
+ }
+
+ access_mask = FILE_GENERIC_READ;
+ share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ create_disposition = FILE_OPEN;
+
+ if (flags & O_RDWR) {
+ access_mask |= FILE_GENERIC_WRITE;
+ share_access &= ~FILE_SHARE_WRITE;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ handle->conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ adp_smb_fname,
+ access_mask,
+ share_access,
+ create_disposition,
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp,
+ NULL, /* psbuf */
+ NULL, NULL); /* create context */
+ TALLOC_FREE(adp_smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+ return -1;
+ }
+
+ *_fsp = fsp;
+ return 0;
+}
+
+/*
+ * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
+ * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
+ * for file IO on the ._ file.
+ */
+static int ad_open(vfs_handle_struct *handle,
+ struct adouble *ad,
+ files_struct *fsp,
+ const struct smb_filename *smb_fname,
+ int flags,
+ mode_t mode)
+{
+ int ret;
+
+ DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
+ ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
+
+ if (ad->ad_type == ADOUBLE_META) {
+ return 0;
+ }
+
+ if (fsp != NULL) {
+ ad->ad_fsp = fsp;
+ ad->ad_opened = false;
+ return 0;
+ }
+
+ ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
+ if (ret != 0) {
+ return -1;
+ }
+ ad->ad_opened = true;
+
+ DBG_DEBUG("Path [%s] type [%s]\n",
+ smb_fname->base_name,
+ ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
+
+ return 0;
+}
+
+static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ char *p_ad = NULL;
+ size_t size;
+ ssize_t len;
+ int ret;
+ bool ok;
+
+ ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
+ if (ret != 0) {
+ DBG_ERR("fstat [%s] failed: %s\n",
+ fsp_str_dbg(ad->ad_fsp), strerror(errno));
+ return -1;
+ }
+
+ /*
+ * AppleDouble file header content and size, two cases:
+ *
+ * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
+ * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
+ *
+ * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
+ */
+ size = ad->ad_fsp->fsp_name->st.st_ex_size;
+ if (size > talloc_array_length(ad->ad_data)) {
+ if (size > AD_XATTR_MAX_HDR_SIZE) {
+ size = AD_XATTR_MAX_HDR_SIZE;
+ }
+ p_ad = talloc_realloc(ad, ad->ad_data, char, size);
+ if (p_ad == NULL) {
+ return -1;
+ }
+ ad->ad_data = p_ad;
+ }
+
+ len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
+ if (len != talloc_array_length(ad->ad_data)) {
+ DBG_NOTICE("%s %s: bad size: %zd\n",
+ smb_fname->base_name, strerror(errno), len);
+ return -1;
+ }
+
+ /* Now parse entries */
+ ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
+ if (!ok) {
+ DBG_ERR("invalid AppleDouble resource %s\n",
+ smb_fname->base_name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
+ || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
+ || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
+ DBG_ERR("invalid AppleDouble resource %s\n",
+ smb_fname->base_name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return len;
+}
+
+/**
+ * Read and parse resource fork, either ._ AppleDouble file or xattr
+ **/
+static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ return ad_read_rsrc_adouble(handle, ad, smb_fname);
+}
+
+/**
+ * Read and unpack an AppleDouble metadata xattr or resource
+ **/
+static ssize_t ad_read(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ switch (ad->ad_type) {
+ case ADOUBLE_META:
+ return ad_read_meta(handle, ad, smb_fname);
+ case ADOUBLE_RSRC:
+ return ad_read_rsrc(handle, ad, smb_fname);
+ default:
+ return -1;
+ }
+}
+
+static int adouble_destructor(struct adouble *ad)
+{
+ NTSTATUS status;
+
+ if (!ad->ad_opened) {
+ return 0;
+ }
+
+ SMB_ASSERT(ad->ad_fsp != NULL);
+
+ status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Closing [%s] failed: %s\n",
+ fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate a struct adouble without initialiing it
+ *
+ * The struct is either hang of the fsp extension context or if fsp is
+ * NULL from ctx.
+ *
+ * @param[in] ctx talloc context
+ * @param[in] handle vfs handle
+ * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return adouble handle
+ **/
+static struct adouble *ad_alloc(TALLOC_CTX *ctx,
+ adouble_type_t type)
+{
+ int rc = 0;
+ size_t adsize = 0;
+ struct adouble *ad;
+
+ switch (type) {
+ case ADOUBLE_META:
+ adsize = AD_DATASZ_XATTR;
+ break;
+ case ADOUBLE_RSRC:
+ adsize = AD_DATASZ_DOT_UND;
+ break;
+ default:
+ return NULL;
+ }
+
+ ad = talloc_zero(ctx, struct adouble);
+ if (ad == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ if (adsize) {
+ ad->ad_data = talloc_zero_array(ad, char, adsize);
+ if (ad->ad_data == NULL) {
+ rc = -1;
+ goto exit;
+ }
+ }
+
+ ad->ad_type = type;
+ ad->ad_magic = AD_MAGIC;
+ ad->ad_version = AD_VERSION;
+
+ talloc_set_destructor(ad, adouble_destructor);
+
+exit:
+ if (rc != 0) {
+ TALLOC_FREE(ad);
+ }
+ return ad;
+}
+
+/**
+ * Allocate and initialize a new struct adouble
+ *
+ * @param[in] ctx talloc context
+ * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return adouble handle, initialized
+ **/
+struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
+{
+ int rc = 0;
+ const struct ad_entry_order *eid;
+ struct adouble *ad = NULL;
+ time_t t = time(NULL);
+
+ switch (type) {
+ case ADOUBLE_META:
+ eid = entry_order_meta_xattr;
+ break;
+ case ADOUBLE_RSRC:
+ eid = entry_order_dot_und;
+ break;
+ default:
+ return NULL;
+ }
+
+ ad = ad_alloc(ctx, type);
+ if (ad == NULL) {
+ return NULL;
+ }
+
+ while (eid->id) {
+ ad->ad_eid[eid->id].ade_off = eid->offset;
+ ad->ad_eid[eid->id].ade_len = eid->len;
+ eid++;
+ }
+
+ /* put something sane in the date fields */
+ ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
+ ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
+ ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
+ ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
+
+ if (rc != 0) {
+ TALLOC_FREE(ad);
+ }
+ return ad;
+}
+
+static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
+ vfs_handle_struct *handle,
+ files_struct *fsp,
+ const struct smb_filename *smb_fname,
+ adouble_type_t type)
+{
+ int rc = 0;
+ ssize_t len;
+ struct adouble *ad = NULL;
+ int mode;
+
+ if (fsp != NULL) {
+ smb_fname = fsp->base_fsp->fsp_name;
+ }
+
+ DEBUG(10, ("ad_get(%s) called for %s\n",
+ type == ADOUBLE_META ? "meta" : "rsrc",
+ smb_fname->base_name));
+
+ ad = ad_alloc(ctx, type);
+ if (ad == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ /* Try rw first so we can use the fd in ad_convert() */
+ mode = O_RDWR;
+
+ rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
+ if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
+ mode = O_RDONLY;
+ rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
+ }
+ if (rc == -1) {
+ DBG_DEBUG("ad_open [%s] error [%s]\n",
+ smb_fname->base_name, strerror(errno));
+ goto exit;
+
+ }
+
+ len = ad_read(handle, ad, smb_fname);
+ if (len == -1) {
+ DEBUG(10, ("error reading AppleDouble for %s\n",
+ smb_fname->base_name));
+ rc = -1;
+ goto exit;
+ }
+
+exit:
+ DEBUG(10, ("ad_get(%s) for %s returning %d\n",
+ type == ADOUBLE_META ? "meta" : "rsrc",
+ smb_fname->base_name, rc));
+
+ if (rc != 0) {
+ TALLOC_FREE(ad);
+ }
+ return ad;
+}
+
+/**
+ * Return AppleDouble data for a file
+ *
+ * @param[in] ctx talloc context
+ * @param[in] handle vfs handle
+ * @param[in] smb_fname pathname to file or directory
+ * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return talloced struct adouble or NULL on error
+ **/
+struct adouble *ad_get(TALLOC_CTX *ctx,
+ vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ adouble_type_t type)
+{
+ return ad_get_internal(ctx, handle, NULL, smb_fname, type);
+}
+
+/**
+ * Return AppleDouble data for a file
+ *
+ * @param[in] ctx talloc context
+ * @param[in] handle vfs handle
+ * @param[in] fsp fsp to use for IO
+ * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return talloced struct adouble or NULL on error
+ **/
+struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
+ files_struct *fsp, adouble_type_t type)
+{
+ return ad_get_internal(ctx, handle, fsp, NULL, type);
+}
+
+/**
+ * Set AppleDouble metadata on a file or directory
+ *
+ * @param[in] ad adouble handle
+ *
+ * @param[in] smb_fname pathname to file or directory
+ *
+ * @return status code, 0 means success
+ **/
+int ad_set(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ bool ok;
+ int ret;
+
+ DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
+
+ if (ad->ad_type != ADOUBLE_META) {
+ DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
+ smb_fname->base_name);
+ return -1;
+ }
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ return -1;
+ }
+
+ ret = SMB_VFS_SETXATTR(handle->conn,
+ smb_fname,
+ AFPINFO_EA_NETATALK,
+ ad->ad_data,
+ AD_DATASZ_XATTR, 0);
+
+ DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
+
+ return ret;
+}
+
+/**
+ * Set AppleDouble metadata on a file or directory
+ *
+ * @param[in] ad adouble handle
+ * @param[in] fsp file handle
+ *
+ * @return status code, 0 means success
+ **/
+int ad_fset(struct vfs_handle_struct *handle,
+ struct adouble *ad,
+ files_struct *fsp)
+{
+ int rc = -1;
+ ssize_t len;
+ bool ok;
+
+ DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
+
+ if ((fsp == NULL)
+ || (fsp->fh == NULL)
+ || (fsp->fh->fd == -1))
+ {
+ smb_panic("bad fsp");
+ }
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ return -1;
+ }
+
+ switch (ad->ad_type) {
+ case ADOUBLE_META:
+ rc = SMB_VFS_NEXT_SETXATTR(handle,
+ fsp->fsp_name,
+ AFPINFO_EA_NETATALK,
+ ad->ad_data,
+ AD_DATASZ_XATTR, 0);
+ break;
+
+ case ADOUBLE_RSRC:
+ len = SMB_VFS_NEXT_PWRITE(handle,
+ fsp,
+ ad->ad_data,
+ AD_DATASZ_DOT_UND,
+ 0);
+ if (len != AD_DATASZ_DOT_UND) {
+ DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
+ return -1;
+ }
+ rc = 0;
+ break;
+
+ default:
+ return -1;
+ }
+
+ DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
+
+ return rc;
+}
+
+bool is_adouble_file(const char *path)
+{
+ const char *p = NULL;
+ int match;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ p = path;
+ } else {
+ p++;
+ }
+
+ match = strncmp(p,
+ ADOUBLE_NAME_PREFIX,
+ strlen(ADOUBLE_NAME_PREFIX));
+ if (match != 0) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Prepend "._" to a basename
+ * Return a new struct smb_filename with stream_name == NULL.
+ **/
+int adouble_path(TALLOC_CTX *ctx,
+ const struct smb_filename *smb_fname_in,
+ struct smb_filename **pp_smb_fname_out)
+{
+ char *parent;
+ const char *base;
+ struct smb_filename *smb_fname = cp_smb_filename(ctx,
+ smb_fname_in);
+
+ if (smb_fname == NULL) {
+ return -1;
+ }
+
+ /* We need streamname to be NULL */
+ TALLOC_FREE(smb_fname->stream_name);
+
+ /* And we're replacing base_name. */
+ TALLOC_FREE(smb_fname->base_name);
+
+ if (!parent_dirname(smb_fname, smb_fname_in->base_name,
+ &parent, &base)) {
+ TALLOC_FREE(smb_fname);
+ return -1;
+ }
+
+ smb_fname->base_name = talloc_asprintf(smb_fname,
+ "%s/._%s", parent, base);
+ if (smb_fname->base_name == NULL) {
+ TALLOC_FREE(smb_fname);
+ return -1;
+ }
+
+ *pp_smb_fname_out = smb_fname;
+
+ return 0;
+}
+
+/**
+ * Allocate and initialize an AfpInfo struct
+ **/
+AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
+{
+ AfpInfo *ai = talloc_zero(ctx, AfpInfo);
+ if (ai == NULL) {
+ return NULL;
+ }
+ ai->afpi_Signature = AFP_Signature;
+ ai->afpi_Version = AFP_Version;
+ ai->afpi_BackupTime = AD_DATE_START;
+ return ai;
+}
+
+/**
+ * Pack an AfpInfo struct into a buffer
+ *
+ * Buffer size must be at least AFP_INFO_SIZE
+ * Returns size of packed buffer
+ **/
+ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
+{
+ memset(buf, 0, AFP_INFO_SIZE);
+
+ RSIVAL(buf, 0, ai->afpi_Signature);
+ RSIVAL(buf, 4, ai->afpi_Version);
+ RSIVAL(buf, 12, ai->afpi_BackupTime);
+ memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
+
+ return AFP_INFO_SIZE;
+}
+
+/**
+ * Unpack a buffer into a AfpInfo structure
+ *
+ * Buffer size must be at least AFP_INFO_SIZE
+ * Returns allocated AfpInfo struct
+ **/
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
+{
+ AfpInfo *ai = talloc_zero(ctx, AfpInfo);
+ if (ai == NULL) {
+ return NULL;
+ }
+
+ ai->afpi_Signature = RIVAL(data, 0);
+ ai->afpi_Version = RIVAL(data, 4);
+ ai->afpi_BackupTime = RIVAL(data, 12);
+ memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
+ sizeof(ai->afpi_FinderInfo));
+
+ if (ai->afpi_Signature != AFP_Signature
+ || ai->afpi_Version != AFP_Version) {
+ DEBUG(1, ("Bad AfpInfo signature or version\n"));
+ TALLOC_FREE(ai);
+ }
+
+ return ai;
+}
diff --git a/source3/lib/adouble.h b/source3/lib/adouble.h
new file mode 100644
index 00000000000..acc9a30d71f
--- /dev/null
+++ b/source3/lib/adouble.h
@@ -0,0 +1,182 @@
+/*
+ * Samba AppleDouble helpers
+ *
+ * Copyright (C) Ralph Boehme, 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ADOUBLE_H_
+#define _ADOUBLE_H_
+
+#include "MacExtensions.h"
+
+#define ADOUBLE_NAME_PREFIX "._"
+
+#define NETATALK_META_XATTR "org.netatalk.Metadata"
+#define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
+
+#if defined(HAVE_ATTROPEN)
+#define AFPINFO_EA_NETATALK NETATALK_META_XATTR
+#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
+#else
+#define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
+#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
+#endif
+
+/*
+ * There are two AppleDouble blobs we deal with:
+ *
+ * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
+ * metadata in an xattr
+ *
+ * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
+ * ._ files
+ */
+typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
+
+/* Version info */
+#define AD_VERSION2 0x00020000
+#define AD_VERSION AD_VERSION2
+
+/*
+ * AppleDouble entry IDs.
+ */
+#define ADEID_DFORK 1
+#define ADEID_RFORK 2
+#define ADEID_NAME 3
+#define ADEID_COMMENT 4
+#define ADEID_ICONBW 5
+#define ADEID_ICONCOL 6
+#define ADEID_FILEI 7
+#define ADEID_FILEDATESI 8
+#define ADEID_FINDERI 9
+#define ADEID_MACFILEI 10
+#define ADEID_PRODOSFILEI 11
+#define ADEID_MSDOSFILEI 12
+#define ADEID_SHORTNAME 13
+#define ADEID_AFPFILEI 14
+#define ADEID_DID 15
+
+/* Private Netatalk entries */
+#define ADEID_PRIVDEV 16
+#define ADEID_PRIVINO 17
+#define ADEID_PRIVSYN 18
+#define ADEID_PRIVID 19
+#define ADEID_MAX (ADEID_PRIVID + 1)
+
+/*
+ * These are the real ids for the private entries,
+ * as stored in the adouble file
+ */
+#define AD_DEV 0x80444556
+#define AD_INO 0x80494E4F
+#define AD_SYN 0x8053594E
+#define AD_ID 0x8053567E
+
+/* AppleDouble magic */
+#define AD_APPLESINGLE_MAGIC 0x00051600
+#define AD_APPLEDOUBLE_MAGIC 0x00051607
+#define AD_MAGIC AD_APPLEDOUBLE_MAGIC
+
+/* Field widths */
+#define ADEDLEN_NAME 255
+#define ADEDLEN_COMMENT 200
+#define ADEDLEN_FILEI 16
+#define ADEDLEN_FINDERI 32
+#define ADEDLEN_FILEDATESI 16
+#define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
+#define ADEDLEN_AFPFILEI 4
+#define ADEDLEN_MACFILEI 4
+#define ADEDLEN_PRODOSFILEI 8
+#define ADEDLEN_MSDOSFILEI 2
+#define ADEDLEN_DID 4
+#define ADEDLEN_PRIVDEV 8
+#define ADEDLEN_PRIVINO 8
+#define ADEDLEN_PRIVSYN 8
+#define ADEDLEN_PRIVID 4
+
+/*
+ * Sharemode locks fcntl() offsets
+ */
+#if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
+#define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
+#else
+#define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
+#endif
+#define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
+
+#define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
+#define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
+#define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
+#define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
+#define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
+#define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
+#define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
+#define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
+#define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
+
+/* Time stuff we overload the bits a little */
+#define AD_DATE_CREATE 0
+#define AD_DATE_MODIFY 4
+#define AD_DATE_BACKUP 8
+#define AD_DATE_ACCESS 12
+#define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
+ AD_DATE_BACKUP | AD_DATE_ACCESS)
+#define AD_DATE_UNIX (1 << 10)
+#define AD_DATE_START 0x80000000
+#define AD_DATE_DELTA 946684800
+#define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
+#define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
+
+#define AD_CONV_WIPE_BLANK (1<<0)
+#define AD_CONV_DELETE (1<<1)
+
+struct adouble;
+
+size_t ad_getentrylen(const struct adouble *ad, int eid);
+size_t ad_getentryoff(const struct adouble *ad, int eid);
+size_t ad_setentrylen(struct adouble *ad, int eid, size_t len);
+size_t ad_setentryoff(struct adouble *ad, int eid, size_t off);
+char *ad_get_entry(const struct adouble *ad, int eid);
+int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date);
+int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date);
+int ad_convert(struct vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ const char *catia_mappings,
+ uint32_t flags);
+struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type);
+struct adouble *ad_get(TALLOC_CTX *ctx,
+ vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ adouble_type_t type);
+struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
+ files_struct *fsp, adouble_type_t type);
+int ad_set(vfs_handle_struct *handle,
+ struct adouble *ad,
+ const struct smb_filename *smb_fname);
+int ad_fset(struct vfs_handle_struct *handle,
+ struct adouble *ad,
+ files_struct *fsp);
+bool is_adouble_file(const char *path);
+int adouble_path(TALLOC_CTX *ctx,
+ const struct smb_filename *smb_fname_in,
+ struct smb_filename **pp_smb_fname_out);
+
+AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
+ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
+
+#endif
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index f05b6cdedc2..2dbbaefba8f 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -33,6 +33,7 @@
#include "offload_token.h"
#include "string_replace.h"
#include "hash_inode.h"
+#include "lib/adouble.h"
/*
* Enhanced OS X and Netatalk compatibility
@@ -105,18 +106,6 @@ static struct global_fruit_config {
#define DBGC_CLASS vfs_fruit_debug_level
#define FRUIT_PARAM_TYPE_NAME "fruit"
-#define ADOUBLE_NAME_PREFIX "._"
-
-#define NETATALK_META_XATTR "org.netatalk.Metadata"
-#define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
-
-#if defined(HAVE_ATTROPEN)
-#define AFPINFO_EA_NETATALK NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
-#else
-#define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
-#endif
enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
@@ -194,303 +183,6 @@ static const char *fruit_catia_maps =
"0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
"0x0d:0xf00d";
-/*****************************************************************************
- * Defines, functions and data structures that deal with AppleDouble
- *****************************************************************************/
-
-/*
- * There are two AppleDouble blobs we deal with:
- *
- * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
- * metadata in an xattr
- *
- * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
- * ._ files
- */
-typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
-
-/* Version info */
-#define AD_VERSION2 0x00020000
-#define AD_VERSION AD_VERSION2
-
-/*
- * AppleDouble entry IDs.
- */
-#define ADEID_DFORK 1
-#define ADEID_RFORK 2
-#define ADEID_NAME 3
-#define ADEID_COMMENT 4
-#define ADEID_ICONBW 5
-#define ADEID_ICONCOL 6
-#define ADEID_FILEI 7
-#define ADEID_FILEDATESI 8
-#define ADEID_FINDERI 9
-#define ADEID_MACFILEI 10
-#define ADEID_PRODOSFILEI 11
-#define ADEID_MSDOSFILEI 12
-#define ADEID_SHORTNAME 13
-#define ADEID_AFPFILEI 14
-#define ADEID_DID 15
-
-/* Private Netatalk entries */
-#define ADEID_PRIVDEV 16
-#define ADEID_PRIVINO 17
-#define ADEID_PRIVSYN 18
-#define ADEID_PRIVID 19
-#define ADEID_MAX (ADEID_PRIVID + 1)
-
-/*
- * These are the real ids for the private entries,
- * as stored in the adouble file
- */
-#define AD_DEV 0x80444556
-#define AD_INO 0x80494E4F
-#define AD_SYN 0x8053594E
-#define AD_ID 0x8053567E
-
-/* Number of actually used entries */
-#define ADEID_NUM_XATTR 8
-#define ADEID_NUM_DOT_UND 2
-#define ADEID_NUM_RSRC_XATTR 1
-
-/* AppleDouble magic */
-#define AD_APPLESINGLE_MAGIC 0x00051600
-#define AD_APPLEDOUBLE_MAGIC 0x00051607
-#define AD_MAGIC AD_APPLEDOUBLE_MAGIC
-
-/* Sizes of relevant entry bits */
-#define ADEDLEN_MAGIC 4
-#define ADEDLEN_VERSION 4
-#define ADEDLEN_FILLER 16
-#define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
-#define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
-#define ADEDLEN_NENTRIES 2
-#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
- ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
-#define AD_ENTRY_LEN_EID 4
-#define AD_ENTRY_LEN_OFF 4
-#define AD_ENTRY_LEN_LEN 4
-#define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
-
-/* Field widths */
-#define ADEDLEN_NAME 255
-#define ADEDLEN_COMMENT 200
-#define ADEDLEN_FILEI 16
-#define ADEDLEN_FINDERI 32
-#define ADEDLEN_FILEDATESI 16
-#define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
-#define ADEDLEN_AFPFILEI 4
-#define ADEDLEN_MACFILEI 4
-#define ADEDLEN_PRODOSFILEI 8
-#define ADEDLEN_MSDOSFILEI 2
-#define ADEDLEN_DID 4
-#define ADEDLEN_PRIVDEV 8
-#define ADEDLEN_PRIVINO 8
-#define ADEDLEN_PRIVSYN 8
-#define ADEDLEN_PRIVID 4
-
-/* Offsets */
-#define ADEDOFF_MAGIC 0
-#define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
-#define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
-#define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
-
-#define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
- (ADEID_NUM_XATTR * AD_ENTRY_LEN))
-#define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
-#define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
-#define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
- ADEDLEN_FILEDATESI)
-#define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
-#define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
-#define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
-#define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
-
-#define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
- (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
-#define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
-
-#define AD_DATASZ_XATTR (AD_HEADER_LEN + \
- (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
- ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
- ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
- ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
- ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
-
-#if AD_DATASZ_XATTR != 402
-#error bad size for AD_DATASZ_XATTR
-#endif
-
-#define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
- (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
- ADEDLEN_FINDERI)
-#if AD_DATASZ_DOT_UND != 82
-#error bad size for AD_DATASZ_DOT_UND
-#endif
-
-/*
- * Sharemode locks fcntl() offsets
- */
-#if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
-#define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
-#else
-#define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
-#endif
-#define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
-
-#define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
-#define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
-#define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
-#define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
-#define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
-#define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
-#define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
-#define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
-#define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
-#define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
-
-/* Time stuff we overload the bits a little */
-#define AD_DATE_CREATE 0
-#define AD_DATE_MODIFY 4
-#define AD_DATE_BACKUP 8
-#define AD_DATE_ACCESS 12
-#define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
- AD_DATE_BACKUP | AD_DATE_ACCESS)
-#define AD_DATE_UNIX (1 << 10)
-#define AD_DATE_START 0x80000000
-#define AD_DATE_DELTA 946684800
-#define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
-#define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
-
-#define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
-#define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
-#define AD_XATTR_HDR_SIZE 36
-#define AD_XATTR_MAX_HDR_SIZE 65536
-
-#define AD_CONV_WIPE_BLANK (1<<0)
-#define AD_CONV_DELETE (1<<1)
-
-/*
- * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
- * representation as well as the on-disk format.
- *
- * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
- * the length of the FinderInfo entry is larger then 32 bytes. It is then
- * preceeded with 2 bytes padding.
- *
- * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
- */
-
-struct ad_xattr_header {
- uint32_t adx_magic; /* ATTR_HDR_MAGIC */
- uint32_t adx_debug_tag; /* for debugging == file id of owning file */
- uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
- uint32_t adx_data_start; /* file offset to attribute data area */
- uint32_t adx_data_length; /* length of attribute data area */
- uint32_t adx_reserved[3];
- uint16_t adx_flags;
- uint16_t adx_num_attrs;
-};
-
-/* On-disk entries are aligned on 4 byte boundaries */
-struct ad_xattr_entry {
- uint32_t adx_offset; /* file offset to data */
- uint32_t adx_length; /* size of attribute data */
- uint16_t adx_flags;
- uint8_t adx_namelen; /* included the NULL terminator */
- char *adx_name; /* NULL-terminated UTF-8 name */
-};
-
-struct ad_entry {
- size_t ade_off;
- size_t ade_len;
-};
-
-struct adouble {
- files_struct *ad_fsp;
- bool ad_opened;
- adouble_type_t ad_type;
- uint32_t ad_magic;
- uint32_t ad_version;
- uint8_t ad_filler[ADEDLEN_FILLER];
- struct ad_entry ad_eid[ADEID_MAX];
- char *ad_data;
- struct ad_xattr_header adx_header;
- struct ad_xattr_entry *adx_entries;
-};
-
-struct ad_entry_order {
- uint32_t id, offset, len;
-};
-
-/* Netatalk AppleDouble metadata xattr */
-static const
-struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
- {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
- {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
- {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
- {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
- {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
- {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
- {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
- {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
- {0, 0, 0}
-};
-
-/* AppleDouble resource fork file (the ones prefixed by "._") */
-static const
-struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
- {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
- {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
- {0, 0, 0}
-};
-
-/* Conversion from enumerated id to on-disk AppleDouble id */
-#define AD_EID_DISK(a) (set_eid[a])
-static const uint32_t set_eid[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- AD_DEV, AD_INO, AD_SYN, AD_ID
-};
-
-static char empty_resourcefork[] = {
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
- 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
- 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
- 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
- 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
- 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
- 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
-};
-
struct fio {
/* tcon config handle */
struct fruit_config_data *config;
@@ -513,1582 +205,6 @@ struct fio {
int mode;
};
-/*
- * Forward declarations
- */
-static struct adouble *ad_init(TALLOC_CTX *ctx,
- adouble_type_t type);
-static struct adouble *ad_get(TALLOC_CTX *ctx,
- vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- adouble_type_t type);
-static int ad_set(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname);
-static int ad_fset(struct vfs_handle_struct *handle,
- struct adouble *ad,
- files_struct *fsp);
-static int adouble_path(TALLOC_CTX *ctx,
- const struct smb_filename *smb_fname__in,
- struct smb_filename **ppsmb_fname_out);
-static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
-static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
-static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
-
-static size_t ad_getentrylen(const struct adouble *ad, int eid)
-{
- return ad->ad_eid[eid].ade_len;
-}
-
-static size_t ad_getentryoff(const struct adouble *ad, int eid)
-{
- return ad->ad_eid[eid].ade_off;
-}
-
-static size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
-{
- return ad->ad_eid[eid].ade_len = len;
-}
-
-static size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
-{
- return ad->ad_eid[eid].ade_off = off;
-}
-
-/**
- * Return a pointer to an AppleDouble entry
- *
- * Returns NULL if the entry is not present
- **/
-static char *ad_get_entry(const struct adouble *ad, int eid)
-{
- off_t off = ad_getentryoff(ad, eid);
- size_t len = ad_getentrylen(ad, eid);
-
- if (off == 0 || len == 0) {
- return NULL;
- }
-
- return ad->ad_data + off;
-}
-
-/**
- * Get a date
- **/
-static int ad_getdate(const struct adouble *ad,
- unsigned int dateoff,
- uint32_t *date)
-{
- bool xlate = (dateoff & AD_DATE_UNIX);
- char *p = NULL;
-
- dateoff &= AD_DATE_MASK;
- p = ad_get_entry(ad, ADEID_FILEDATESI);
- if (p == NULL) {
- return -1;
- }
-
- if (dateoff > AD_DATE_ACCESS) {
- return -1;
- }
-
- memcpy(date, p + dateoff, sizeof(uint32_t));
-
- if (xlate) {
- *date = AD_DATE_TO_UNIX(*date);
- }
- return 0;
-}
-
-/**
- * Set a date
- **/
-static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
-{
- bool xlate = (dateoff & AD_DATE_UNIX);
- char *p = NULL;
-
- p = ad_get_entry(ad, ADEID_FILEDATESI);
- if (p == NULL) {
- return -1;
- }
-
- dateoff &= AD_DATE_MASK;
- if (xlate) {
- date = AD_DATE_FROM_UNIX(date);
- }
-
- if (dateoff > AD_DATE_ACCESS) {
- return -1;
- }
-
- memcpy(p + dateoff, &date, sizeof(date));
-
- return 0;
-}
-
-
-/**
- * Map on-disk AppleDouble id to enumerated id
- **/
-static uint32_t get_eid(uint32_t eid)
-{
- if (eid <= 15) {
- return eid;
- }
-
- switch (eid) {
- case AD_DEV:
- return ADEID_PRIVDEV;
- case AD_INO:
- return ADEID_PRIVINO;
- case AD_SYN:
- return ADEID_PRIVSYN;
- case AD_ID:
- return ADEID_PRIVID;
- default:
- break;
- }
-
- return 0;
-}
-
-/**
- * Pack AppleDouble structure into data buffer
- **/
-static bool ad_pack(struct adouble *ad)
-{
- uint32_t eid;
- uint16_t nent;
- uint32_t bufsize;
- uint32_t offset = 0;
-
- bufsize = talloc_get_size(ad->ad_data);
- if (bufsize < AD_DATASZ_DOT_UND) {
- DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
- return false;
- }
-
- if (offset + ADEDLEN_MAGIC < offset ||
- offset + ADEDLEN_MAGIC >= bufsize) {
- return false;
- }
- RSIVAL(ad->ad_data, offset, ad->ad_magic);
- offset += ADEDLEN_MAGIC;
-
- if (offset + ADEDLEN_VERSION < offset ||
- offset + ADEDLEN_VERSION >= bufsize) {
- return false;
- }
- RSIVAL(ad->ad_data, offset, ad->ad_version);
- offset += ADEDLEN_VERSION;
-
- if (offset + ADEDLEN_FILLER < offset ||
- offset + ADEDLEN_FILLER >= bufsize) {
- return false;
- }
- if (ad->ad_type == ADOUBLE_RSRC) {
- memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
- }
- offset += ADEDLEN_FILLER;
-
- if (offset + ADEDLEN_NENTRIES < offset ||
- offset + ADEDLEN_NENTRIES >= bufsize) {
- return false;
- }
- offset += ADEDLEN_NENTRIES;
-
- for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
- if (ad->ad_eid[eid].ade_off == 0) {
- /*
- * ade_off is also used as indicator whether a
- * specific entry is used or not
- */
- continue;
- }
-
- if (offset + AD_ENTRY_LEN_EID < offset ||
- offset + AD_ENTRY_LEN_EID >= bufsize) {
- return false;
- }
- RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
- offset += AD_ENTRY_LEN_EID;
-
- if (offset + AD_ENTRY_LEN_OFF < offset ||
- offset + AD_ENTRY_LEN_OFF >= bufsize) {
- return false;
- }
- RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
- offset += AD_ENTRY_LEN_OFF;
-
- if (offset + AD_ENTRY_LEN_LEN < offset ||
- offset + AD_ENTRY_LEN_LEN >= bufsize) {
- return false;
- }
- RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
- offset += AD_ENTRY_LEN_LEN;
-
- nent++;
- }
-
- if (ADEDOFF_NENTRIES + 2 >= bufsize) {
- return false;
- }
- RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
-
- return true;
-}
-
-static bool ad_unpack_xattrs(struct adouble *ad)
-{
- struct ad_xattr_header *h = &ad->adx_header;
- const char *p = ad->ad_data;
- uint32_t hoff;
- uint32_t i;
-
- if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
- return true;
- }
-
- /* 2 bytes padding */
- hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
-
- h->adx_magic = RIVAL(p, hoff + 0);
- h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
- h->adx_total_size = RIVAL(p, hoff + 8);
- h->adx_data_start = RIVAL(p, hoff + 12);
- h->adx_data_length = RIVAL(p, hoff + 16);
- h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
- h->adx_num_attrs = RSVAL(p, hoff + 34);
-
- if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
- DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
- return false;
- }
-
- if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
- DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
- return false;
- }
- if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
- DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
- return false;
- }
-
- if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
- DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
- return false;
- }
-
- if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
- DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
- return false;
- }
- if ((h->adx_data_start + h->adx_data_length) >
- ad->adx_header.adx_total_size)
- {
- DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
- return false;
- }
-
- if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
- DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
- return false;
- }
-
- if (h->adx_num_attrs == 0) {
- return true;
- }
-
- ad->adx_entries = talloc_zero_array(
- ad, struct ad_xattr_entry, h->adx_num_attrs);
- if (ad->adx_entries == NULL) {
- return false;
- }
-
- hoff += AD_XATTR_HDR_SIZE;
-
- for (i = 0; i < h->adx_num_attrs; i++) {
- struct ad_xattr_entry *e = &ad->adx_entries[i];
-
- hoff = (hoff + 3) & ~3;
-
- e->adx_offset = RIVAL(p, hoff + 0);
- e->adx_length = RIVAL(p, hoff + 4);
- e->adx_flags = RSVAL(p, hoff + 8);
- e->adx_namelen = *(p + hoff + 10);
-
- if (e->adx_offset >= ad->adx_header.adx_total_size) {
- DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
- e->adx_offset);
- return false;
- }
-
- if ((e->adx_offset + e->adx_length) < e->adx_offset) {
- DBG_ERR("Bad adx_length: %" PRIx32 "\n",
- e->adx_length);
- return false;
- }
-
- if ((e->adx_offset + e->adx_length) >
- ad->adx_header.adx_total_size)
- {
- DBG_ERR("Bad adx_length: %" PRIx32 "\n",
- e->adx_length);
- return false;
- }
-
- if (e->adx_namelen == 0) {
- DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
- e->adx_namelen);
- return false;
- }
- if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
- DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
- e->adx_namelen);
- return false;
- }
- if ((hoff + 11 + e->adx_namelen) >
- ad->adx_header.adx_data_start)
- {
- DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
- e->adx_namelen);
- return false;
- }
-
- e->adx_name = talloc_strndup(ad->adx_entries,
- p + hoff + 11,
- e->adx_namelen);
- if (e->adx_name == NULL) {
- return false;
- }
-
- DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
- e->adx_name, e->adx_offset, e->adx_length);
- dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
- e->adx_length);
-
- hoff += 11 + e->adx_namelen;
- }
-
- return true;
-}
-
-/**
- * Unpack an AppleDouble blob into a struct adoble
- **/
-static bool ad_unpack(struct adouble *ad, const size_t nentries,
- size_t filesize)
-{
- size_t bufsize = talloc_get_size(ad->ad_data);
- size_t adentries, i;
- uint32_t eid, len, off;
- bool ok;
-
- /*
- * The size of the buffer ad->ad_data is checked when read, so
- * we wouldn't have to check our own offsets, a few extra
- * checks won't hurt though. We have to check the offsets we
- * read from the buffer anyway.
- */
-
- if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
- DEBUG(1, ("bad size\n"));
- return false;
- }
-
- ad->ad_magic = RIVAL(ad->ad_data, 0);
- ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
- if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
- DEBUG(1, ("wrong magic or version\n"));
- return false;
- }
-
- memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
-
- adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
- if (adentries != nentries) {
- DEBUG(1, ("invalid number of entries: %zu\n",
- adentries));
- return false;
- }
-
- /* now, read in the entry bits */
- for (i = 0; i < adentries; i++) {
- eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
- eid = get_eid(eid);
- off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
- len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
-
- if (!eid || eid >= ADEID_MAX) {
- DEBUG(1, ("bogus eid %d\n", eid));
- return false;
- }
-
- /*
- * All entries other than the resource fork are
- * expected to be read into the ad_data buffer, so
- * ensure the specified offset is within that bound
- */
- if ((off > bufsize) && (eid != ADEID_RFORK)) {
- DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
- eid, off, len));
- return false;
- }
-
- /*
- * All entries besides FinderInfo and resource fork
- * must fit into the buffer. FinderInfo is special as
- * it may be larger then the default 32 bytes (if it
- * contains marshalled xattrs), but we will fixup that
- * in ad_convert(). And the resource fork is never
- * accessed directly by the ad_data buf (also see
- * comment above) anyway.
- */
- if ((eid != ADEID_RFORK) &&
- (eid != ADEID_FINDERI) &&
- ((off + len) > bufsize)) {
- DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
- eid, off, len));
- return false;
- }
-
- /*
- * That would be obviously broken
- */
- if (off > filesize) {
- DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
- eid, off, len));
- return false;
- }
-
- /*
- * Check for any entry that has its end beyond the
- * filesize.
- */
- if (off + len < off) {
- DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
- ", len: %" PRIu32 "\n",
- eid, off, len));
- return false;
-
- }
- if (off + len > filesize) {
- /*
- * If this is the resource fork entry, we fix
- * up the length, for any other entry we bail
- * out.
- */
- if (eid != ADEID_RFORK) {
- DEBUG(1, ("bogus eid %d: off: %" PRIu32
- ", len: %" PRIu32 "\n",
- eid, off, len));
- return false;
- }
-
- /*
- * Fixup the resource fork entry by limiting
- * the size to entryoffset - filesize.
- */
- len = filesize - off;
- DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
- ", len: %" PRIu32 "\n", off, len));
- }
-
- ad->ad_eid[eid].ade_off = off;
- ad->ad_eid[eid].ade_len = len;
- }
-
- ok = ad_unpack_xattrs(ad);
- if (!ok) {
- return false;
- }
-
- return true;
-}
-
-static bool ad_convert_move_reso(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- char *buf = NULL;
- size_t rforklen;
- size_t rforkoff;
- ssize_t n;
- int ret;
- bool ok;
-
- rforklen = ad_getentrylen(ad, ADEID_RFORK);
- if (rforklen == 0) {
- return true;
- }
-
- buf = talloc_size(ad, rforklen);
- if (buf == NULL) {
- /*
- * This allocates a buffer for reading the resource fork data in
- * one big swoop. Resource forks won't be larger then, say, 64
- * MB, I swear, so just doing the allocation with the talloc
- * limit as safeguard seems safe.
- */
- DBG_ERR("Failed to allocate %zu bytes for rfork\n",
- rforklen);
- return false;
- }
-
- rforkoff = ad_getentryoff(ad, ADEID_RFORK);
-
- n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
- if (n != rforklen) {
- DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
- rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
- return false;
- }
-
- rforkoff = ADEDOFF_RFORK_DOT_UND;
-
- n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
- if (n != rforklen) {
- DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
- rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
- return false;
- }
-
- ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
- ok = ad_pack(ad);
- if (!ok) {
- DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
- return false;
- }
-
- ret = ad_fset(handle, ad, ad->ad_fsp);
- if (ret != 0) {
- DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
- return false;
- }
-
- return true;
-}
-
-static bool ad_convert_xattr(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname,
- const char *catia_mappings,
- bool *converted_xattr)
-{
- static struct char_mappings **string_replace_cmaps = NULL;
- uint16_t i;
- int saved_errno = 0;
- NTSTATUS status;
- int rc;
- bool ok;
-
- *converted_xattr = false;
-
- if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
- return true;
- }
-
- if (string_replace_cmaps == NULL) {
- const char **mappings = NULL;
-
- mappings = str_list_make_v3_const(
- talloc_tos(), catia_mappings, NULL);
- if (mappings == NULL) {
- return false;
- }
- string_replace_cmaps = string_replace_init_map(mappings);
- TALLOC_FREE(mappings);
- }
-
- for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
- struct ad_xattr_entry *e = &ad->adx_entries[i];
- char *mapped_name = NULL;
- char *tmp = NULL;
- struct smb_filename *stream_name = NULL;
- files_struct *fsp = NULL;
- ssize_t nwritten;
-
- status = string_replace_allocate(handle->conn,
- e->adx_name,
- string_replace_cmaps,
- talloc_tos(),
- &mapped_name,
- vfs_translate_to_windows);
- if (!NT_STATUS_IS_OK(status) &&
- !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
- {
- DBG_ERR("string_replace_allocate failed\n");
- ok = false;
- goto fail;
- }
-
- tmp = mapped_name;
- mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
- TALLOC_FREE(tmp);
- if (mapped_name == NULL) {
- ok = false;
- goto fail;
- }
-
- stream_name = synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
- mapped_name,
- NULL,
- smb_fname->flags);
- TALLOC_FREE(mapped_name);
- if (stream_name == NULL) {
- DBG_ERR("synthetic_smb_fname failed\n");
- ok = false;
- goto fail;
- }
-
- DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
-
- status = SMB_VFS_CREATE_FILE(
- handle->conn, /* conn */
- NULL, /* req */
- 0, /* root_dir_fid */
- stream_name, /* fname */
- FILE_GENERIC_WRITE, /* access_mask */
- FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
- FILE_OPEN_IF, /* create_disposition */
- 0, /* create_options */
- 0, /* file_attributes */
- INTERNAL_OPEN_ONLY, /* oplock_request */
- NULL, /* lease */
- 0, /* allocation_size */
- 0, /* private_flags */
- NULL, /* sd */
- NULL, /* ea_list */
- &fsp, /* result */
- NULL, /* psbuf */
- NULL, NULL); /* create context */
- TALLOC_FREE(stream_name);
- if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
- ok = false;
- goto fail;
- }
-
- nwritten = SMB_VFS_PWRITE(fsp,
- ad->ad_data + e->adx_offset,
- e->adx_length,
- 0);
- if (nwritten == -1) {
- DBG_ERR("SMB_VFS_PWRITE failed\n");
- saved_errno = errno;
- close_file(NULL, fsp, ERROR_CLOSE);
- errno = saved_errno;
- ok = false;
- goto fail;
- }
-
- status = close_file(NULL, fsp, NORMAL_CLOSE);
- if (!NT_STATUS_IS_OK(status)) {
- ok = false;
- goto fail;
- }
- fsp = NULL;
- }
-
- ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
-
- ok = ad_pack(ad);
- if (!ok) {
- DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
- goto fail;
- }
-
- rc = ad_fset(handle, ad, ad->ad_fsp);
- if (rc != 0) {
- DBG_ERR("ad_fset on [%s] failed: %s\n",
- fsp_str_dbg(ad->ad_fsp), strerror(errno));
- ok = false;
- goto fail;
- }
-
- ok = ad_convert_move_reso(handle, ad, smb_fname);
- if (!ok) {
- goto fail;
- }
-
- *converted_xattr = true;
- ok = true;
-
-fail:
- return ok;
-}
-
-static bool ad_convert_finderinfo(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- char *p_ad = NULL;
- AfpInfo *ai = NULL;
- DATA_BLOB aiblob;
- struct smb_filename *stream_name = NULL;
- files_struct *fsp = NULL;
- size_t size;
- ssize_t nwritten;
- NTSTATUS status;
- int saved_errno = 0;
- int cmp;
-
- cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
- if (cmp != 0) {
- return true;
- }
-
- p_ad = ad_get_entry(ad, ADEID_FINDERI);
- if (p_ad == NULL) {
- return false;
- }
-
- ai = afpinfo_new(talloc_tos());
- if (ai == NULL) {
- return false;
- }
-
- memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
-
- aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
- if (aiblob.data == NULL) {
- TALLOC_FREE(ai);
- return false;
- }
-
- size = afpinfo_pack(ai, (char *)aiblob.data);
- TALLOC_FREE(ai);
- if (size != AFP_INFO_SIZE) {
- return false;
- }
-
- stream_name = synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
- AFPINFO_STREAM,
- NULL,
- smb_fname->flags);
- if (stream_name == NULL) {
- data_blob_free(&aiblob);
- DBG_ERR("synthetic_smb_fname failed\n");
- return false;
- }
-
- DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
-
- status = SMB_VFS_CREATE_FILE(
- handle->conn, /* conn */
- NULL, /* req */
- 0, /* root_dir_fid */
- stream_name, /* fname */
- FILE_GENERIC_WRITE, /* access_mask */
- FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
- FILE_OPEN_IF, /* create_disposition */
- 0, /* create_options */
- 0, /* file_attributes */
- INTERNAL_OPEN_ONLY, /* oplock_request */
- NULL, /* lease */
- 0, /* allocation_size */
- 0, /* private_flags */
- NULL, /* sd */
- NULL, /* ea_list */
- &fsp, /* result */
- NULL, /* psbuf */
- NULL, NULL); /* create context */
- TALLOC_FREE(stream_name);
- if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
- return false;
- }
-
- nwritten = SMB_VFS_PWRITE(fsp,
- aiblob.data,
- aiblob.length,
- 0);
- if (nwritten == -1) {
- DBG_ERR("SMB_VFS_PWRITE failed\n");
- saved_errno = errno;
- close_file(NULL, fsp, ERROR_CLOSE);
- errno = saved_errno;
- return false;
- }
-
- status = close_file(NULL, fsp, NORMAL_CLOSE);
- if (!NT_STATUS_IS_OK(status)) {
- return false;
- }
- fsp = NULL;
-
- return true;
-}
-
-static bool ad_convert_truncate(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- int rc;
- off_t newlen;
-
- newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
-
- rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
- if (rc != 0) {
- return false;
- }
-
- return true;
-}
-
-static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
- struct adouble *ad,
- uint32_t flags,
- bool *blank)
-{
- size_t rforklen = sizeof(empty_resourcefork);
- char buf[rforklen];
- ssize_t nread;
- int cmp;
- int rc;
- bool ok;
-
- *blank = false;
-
- if (!(flags & AD_CONV_WIPE_BLANK)) {
- return true;
- }
-
- if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
- return true;
- }
-
- nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
- if (nread != rforklen) {
- DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
- rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
- return false;
- }
-
- cmp = memcmp(buf, empty_resourcefork, rforklen);
- if (cmp != 0) {
- return true;
- }
-
- ad_setentrylen(ad, ADEID_RFORK, 0);
- ok = ad_pack(ad);
- if (!ok) {
- return false;
- }
-
- rc = ad_fset(handle, ad, ad->ad_fsp);
- if (rc != 0) {
- DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
- return false;
- }
-
- *blank = true;
- return true;
-}
-
-static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname,
- uint32_t flags)
-{
- struct smb_filename *ad_name = NULL;
- int rc;
-
- if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
- return true;
- }
-
- if (!(flags & AD_CONV_DELETE)) {
- return true;
- }
-
- rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
- if (rc != 0) {
- return false;
- }
-
- rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
- if (rc != 0) {
- DBG_ERR("Unlinking [%s] failed: %s\n",
- smb_fname_str_dbg(ad_name), strerror(errno));
- TALLOC_FREE(ad_name);
- return false;
- }
-
- DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
- TALLOC_FREE(ad_name);
-
- return true;
-}
-
-/**
- * Convert from Apple's ._ file to Netatalk
- *
- * Apple's AppleDouble may contain a FinderInfo entry longer then 32
- * bytes containing packed xattrs.
- *
- * @return -1 in case an error occurred, 0 if no conversion was done, 1
- * otherwise
- **/
-static int ad_convert(struct vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- const char *catia_mappings,
- uint32_t flags)
-{
- struct adouble *ad = NULL;
- bool ok;
- bool converted_xattr = false;
- bool blank;
- int ret;
-
- ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
- if (ad == NULL) {
- return 0;
- }
-
- ok = ad_convert_xattr(handle,
- ad,
- smb_fname,
- catia_mappings,
- &converted_xattr);
- if (!ok) {
- ret = -1;
- goto done;
- }
-
- ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
- if (!ok) {
- ret = -1;
- goto done;
- }
-
- if (converted_xattr || blank) {
- ok = ad_convert_truncate(handle, ad, smb_fname);
- if (!ok) {
- ret = -1;
- goto done;
- }
- }
-
- ok = ad_convert_finderinfo(handle, ad, smb_fname);
- if (!ok) {
- DBG_ERR("Failed to convert [%s]\n",
- smb_fname_str_dbg(smb_fname));
- ret = -1;
- goto done;
- }
-
- ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
- if (!ok) {
- ret = -1;
- goto done;
- }
-
- ret = 0;
-done:
- TALLOC_FREE(ad);
- return ret;
-}
-
-/**
- * Read and parse Netatalk AppleDouble metadata xattr
- **/
-static ssize_t ad_read_meta(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- int rc = 0;
- ssize_t ealen;
- bool ok;
-
- DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
-
- ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
- AFPINFO_EA_NETATALK, ad->ad_data,
- AD_DATASZ_XATTR);
- if (ealen == -1) {
- switch (errno) {
- case ENOATTR:
- case ENOENT:
- if (errno == ENOATTR) {
- errno = ENOENT;
- }
- rc = -1;
- goto exit;
- default:
- DEBUG(2, ("error reading meta xattr: %s\n",
- strerror(errno)));
- rc = -1;
- goto exit;
- }
- }
- if (ealen != AD_DATASZ_XATTR) {
- DEBUG(2, ("bad size %zd\n", ealen));
- errno = EINVAL;
- rc = -1;
- goto exit;
- }
-
- /* Now parse entries */
- ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
- if (!ok) {
- DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
- errno = EINVAL;
- rc = -1;
- goto exit;
- }
-
- if (!ad_getentryoff(ad, ADEID_FINDERI)
- || !ad_getentryoff(ad, ADEID_COMMENT)
- || !ad_getentryoff(ad, ADEID_FILEDATESI)
- || !ad_getentryoff(ad, ADEID_AFPFILEI)
- || !ad_getentryoff(ad, ADEID_PRIVDEV)
- || !ad_getentryoff(ad, ADEID_PRIVINO)
- || !ad_getentryoff(ad, ADEID_PRIVSYN)
- || !ad_getentryoff(ad, ADEID_PRIVID)) {
- DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
- errno = EINVAL;
- rc = -1;
- goto exit;
- }
-
-exit:
- DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
- smb_fname->base_name, rc));
-
- if (rc != 0) {
- ealen = -1;
- if (errno == EINVAL) {
- become_root();
- (void)SMB_VFS_REMOVEXATTR(handle->conn,
- smb_fname,
- AFPINFO_EA_NETATALK);
- unbecome_root();
- errno = ENOENT;
- }
- }
- return ealen;
-}
-
-static int ad_open_rsrc(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- int flags,
- mode_t mode,
- files_struct **_fsp)
-{
- int ret;
- struct smb_filename *adp_smb_fname = NULL;
- files_struct *fsp = NULL;
- uint32_t access_mask;
- uint32_t share_access;
- uint32_t create_disposition;
- NTSTATUS status;
-
- ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
- if (ret != 0) {
- return -1;
- }
-
- ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
- if (ret != 0) {
- TALLOC_FREE(adp_smb_fname);
- return -1;
- }
-
- access_mask = FILE_GENERIC_READ;
- share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
- create_disposition = FILE_OPEN;
-
- if (flags & O_RDWR) {
- access_mask |= FILE_GENERIC_WRITE;
- share_access &= ~FILE_SHARE_WRITE;
- }
-
- status = SMB_VFS_CREATE_FILE(
- handle->conn, /* conn */
- NULL, /* req */
- 0, /* root_dir_fid */
- adp_smb_fname,
- access_mask,
- share_access,
- create_disposition,
- 0, /* create_options */
- 0, /* file_attributes */
- INTERNAL_OPEN_ONLY, /* oplock_request */
- NULL, /* lease */
- 0, /* allocation_size */
- 0, /* private_flags */
- NULL, /* sd */
- NULL, /* ea_list */
- &fsp,
- NULL, /* psbuf */
- NULL, NULL); /* create context */
- TALLOC_FREE(adp_smb_fname);
- if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
- return -1;
- }
-
- *_fsp = fsp;
- return 0;
-}
-
-/*
- * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
- * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
- * for file IO on the ._ file.
- */
-static int ad_open(vfs_handle_struct *handle,
- struct adouble *ad,
- files_struct *fsp,
- const struct smb_filename *smb_fname,
- int flags,
- mode_t mode)
-{
- int ret;
-
- DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
- ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
-
- if (ad->ad_type == ADOUBLE_META) {
- return 0;
- }
-
- if (fsp != NULL) {
- ad->ad_fsp = fsp;
- ad->ad_opened = false;
- return 0;
- }
-
- ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
- if (ret != 0) {
- return -1;
- }
- ad->ad_opened = true;
-
- DBG_DEBUG("Path [%s] type [%s]\n",
- smb_fname->base_name,
- ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
-
- return 0;
-}
-
-static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- char *p_ad = NULL;
- size_t size;
- ssize_t len;
- int ret;
- bool ok;
-
- ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
- if (ret != 0) {
- DBG_ERR("fstat [%s] failed: %s\n",
- fsp_str_dbg(ad->ad_fsp), strerror(errno));
- return -1;
- }
-
- /*
- * AppleDouble file header content and size, two cases:
- *
- * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
- * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
- *
- * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
- */
- size = ad->ad_fsp->fsp_name->st.st_ex_size;
- if (size > talloc_array_length(ad->ad_data)) {
- if (size > AD_XATTR_MAX_HDR_SIZE) {
- size = AD_XATTR_MAX_HDR_SIZE;
- }
- p_ad = talloc_realloc(ad, ad->ad_data, char, size);
- if (p_ad == NULL) {
- return -1;
- }
- ad->ad_data = p_ad;
- }
-
- len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
- if (len != talloc_array_length(ad->ad_data)) {
- DBG_NOTICE("%s %s: bad size: %zd\n",
- smb_fname->base_name, strerror(errno), len);
- return -1;
- }
-
- /* Now parse entries */
- ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
- if (!ok) {
- DBG_ERR("invalid AppleDouble resource %s\n",
- smb_fname->base_name);
- errno = EINVAL;
- return -1;
- }
-
- if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
- || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
- || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
- DBG_ERR("invalid AppleDouble resource %s\n",
- smb_fname->base_name);
- errno = EINVAL;
- return -1;
- }
-
- return len;
-}
-
-/**
- * Read and parse resource fork, either ._ AppleDouble file or xattr
- **/
-static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- return ad_read_rsrc_adouble(handle, ad, smb_fname);
-}
-
-/**
- * Read and unpack an AppleDouble metadata xattr or resource
- **/
-static ssize_t ad_read(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- switch (ad->ad_type) {
- case ADOUBLE_META:
- return ad_read_meta(handle, ad, smb_fname);
- case ADOUBLE_RSRC:
- return ad_read_rsrc(handle, ad, smb_fname);
- default:
- return -1;
- }
-}
-
-static int adouble_destructor(struct adouble *ad)
-{
- NTSTATUS status;
-
- if (!ad->ad_opened) {
- return 0;
- }
-
- SMB_ASSERT(ad->ad_fsp != NULL);
-
- status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
- if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("Closing [%s] failed: %s\n",
- fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
- }
-
- return 0;
-}
-
-/**
- * Allocate a struct adouble without initialiing it
- *
- * The struct is either hang of the fsp extension context or if fsp is
- * NULL from ctx.
- *
- * @param[in] ctx talloc context
- * @param[in] handle vfs handle
- * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return adouble handle
- **/
-static struct adouble *ad_alloc(TALLOC_CTX *ctx,
- adouble_type_t type)
-{
- int rc = 0;
- size_t adsize = 0;
- struct adouble *ad;
-
- switch (type) {
- case ADOUBLE_META:
- adsize = AD_DATASZ_XATTR;
- break;
- case ADOUBLE_RSRC:
- adsize = AD_DATASZ_DOT_UND;
- break;
- default:
- return NULL;
- }
-
- ad = talloc_zero(ctx, struct adouble);
- if (ad == NULL) {
- rc = -1;
- goto exit;
- }
-
- if (adsize) {
- ad->ad_data = talloc_zero_array(ad, char, adsize);
- if (ad->ad_data == NULL) {
- rc = -1;
- goto exit;
- }
- }
-
- ad->ad_type = type;
- ad->ad_magic = AD_MAGIC;
- ad->ad_version = AD_VERSION;
-
- talloc_set_destructor(ad, adouble_destructor);
-
-exit:
- if (rc != 0) {
- TALLOC_FREE(ad);
- }
- return ad;
-}
-
-/**
- * Allocate and initialize a new struct adouble
- *
- * @param[in] ctx talloc context
- * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return adouble handle, initialized
- **/
-static struct adouble *ad_init(TALLOC_CTX *ctx,
- adouble_type_t type)
-{
- int rc = 0;
- const struct ad_entry_order *eid;
- struct adouble *ad = NULL;
- time_t t = time(NULL);
-
- switch (type) {
- case ADOUBLE_META:
- eid = entry_order_meta_xattr;
- break;
- case ADOUBLE_RSRC:
- eid = entry_order_dot_und;
- break;
- default:
- return NULL;
- }
-
- ad = ad_alloc(ctx, type);
- if (ad == NULL) {
- return NULL;
- }
-
- while (eid->id) {
- ad->ad_eid[eid->id].ade_off = eid->offset;
- ad->ad_eid[eid->id].ade_len = eid->len;
- eid++;
- }
-
- /* put something sane in the date fields */
- ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
- ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
- ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
- ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
-
- if (rc != 0) {
- TALLOC_FREE(ad);
- }
- return ad;
-}
-
-static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
- vfs_handle_struct *handle,
- files_struct *fsp,
- const struct smb_filename *smb_fname,
- adouble_type_t type)
-{
- int rc = 0;
- ssize_t len;
- struct adouble *ad = NULL;
- int mode;
-
- if (fsp != NULL) {
- smb_fname = fsp->base_fsp->fsp_name;
- }
-
- DEBUG(10, ("ad_get(%s) called for %s\n",
- type == ADOUBLE_META ? "meta" : "rsrc",
- smb_fname->base_name));
-
- ad = ad_alloc(ctx, type);
- if (ad == NULL) {
- rc = -1;
- goto exit;
- }
-
- /* Try rw first so we can use the fd in ad_convert() */
- mode = O_RDWR;
-
- rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
- if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
- mode = O_RDONLY;
- rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
- }
- if (rc == -1) {
- DBG_DEBUG("ad_open [%s] error [%s]\n",
- smb_fname->base_name, strerror(errno));
- goto exit;
-
- }
-
- len = ad_read(handle, ad, smb_fname);
- if (len == -1) {
- DEBUG(10, ("error reading AppleDouble for %s\n",
- smb_fname->base_name));
- rc = -1;
- goto exit;
- }
-
-exit:
- DEBUG(10, ("ad_get(%s) for %s returning %d\n",
- type == ADOUBLE_META ? "meta" : "rsrc",
- smb_fname->base_name, rc));
-
- if (rc != 0) {
- TALLOC_FREE(ad);
- }
- return ad;
-}
-
-/**
- * Return AppleDouble data for a file
- *
- * @param[in] ctx talloc context
- * @param[in] handle vfs handle
- * @param[in] smb_fname pathname to file or directory
- * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return talloced struct adouble or NULL on error
- **/
-static struct adouble *ad_get(TALLOC_CTX *ctx,
- vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- adouble_type_t type)
-{
- return ad_get_internal(ctx, handle, NULL, smb_fname, type);
-}
-
-/**
- * Return AppleDouble data for a file
- *
- * @param[in] ctx talloc context
- * @param[in] handle vfs handle
- * @param[in] fsp fsp to use for IO
- * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return talloced struct adouble or NULL on error
- **/
-static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
- files_struct *fsp, adouble_type_t type)
-{
- return ad_get_internal(ctx, handle, fsp, NULL, type);
-}
-
-/**
- * Set AppleDouble metadata on a file or directory
- *
- * @param[in] ad adouble handle
- *
- * @param[in] smb_fname pathname to file or directory
- *
- * @return status code, 0 means success
- **/
-static int ad_set(vfs_handle_struct *handle,
- struct adouble *ad,
- const struct smb_filename *smb_fname)
-{
- bool ok;
- int ret;
-
- DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
-
- if (ad->ad_type != ADOUBLE_META) {
- DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
- smb_fname->base_name);
- return -1;
- }
-
- ok = ad_pack(ad);
- if (!ok) {
- return -1;
- }
-
- ret = SMB_VFS_SETXATTR(handle->conn,
- smb_fname,
- AFPINFO_EA_NETATALK,
- ad->ad_data,
- AD_DATASZ_XATTR, 0);
-
- DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
-
- return ret;
-}
-
-/**
- * Set AppleDouble metadata on a file or directory
- *
- * @param[in] ad adouble handle
- * @param[in] fsp file handle
- *
- * @return status code, 0 means success
- **/
-static int ad_fset(struct vfs_handle_struct *handle,
- struct adouble *ad,
- files_struct *fsp)
-{
- int rc = -1;
- ssize_t len;
- bool ok;
-
- DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
-
- if ((fsp == NULL)
- || (fsp->fh == NULL)
- || (fsp->fh->fd == -1))
- {
- smb_panic("bad fsp");
- }
-
- ok = ad_pack(ad);
- if (!ok) {
- return -1;
- }
-
- switch (ad->ad_type) {
- case ADOUBLE_META:
- rc = SMB_VFS_NEXT_SETXATTR(handle,
- fsp->fsp_name,
- AFPINFO_EA_NETATALK,
- ad->ad_data,
- AD_DATASZ_XATTR, 0);
- break;
-
- case ADOUBLE_RSRC:
- len = SMB_VFS_NEXT_PWRITE(handle,
- fsp,
- ad->ad_data,
- AD_DATASZ_DOT_UND,
- 0);
- if (len != AD_DATASZ_DOT_UND) {
- DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
- return -1;
- }
- rc = 0;
- break;
-
- default:
- return -1;
- }
-
- DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
-
- return rc;
-}
-
/*****************************************************************************
* Helper functions
*****************************************************************************/
@@ -2127,27 +243,6 @@ static bool is_apple_stream(const struct smb_filename *smb_fname)
return false;
}
-static bool is_adouble_file(const char *path)
-{
- const char *p = NULL;
- int match;
-
- p = strrchr(path, '/');
- if (p == NULL) {
- p = path;
- } else {
- p++;
- }
-
- match = strncmp(p,
- ADOUBLE_NAME_PREFIX,
- strlen(ADOUBLE_NAME_PREFIX));
- if (match != 0) {
- return false;
- }
- return true;
-}
-
/**
* Initialize config struct from our smb.conf config parameters
**/
@@ -2277,108 +372,6 @@ static int init_fruit_config(vfs_handle_struct *handle)
return 0;
}
-/**
- * Prepend "._" to a basename
- * Return a new struct smb_filename with stream_name == NULL.
- **/
-static int adouble_path(TALLOC_CTX *ctx,
- const struct smb_filename *smb_fname_in,
- struct smb_filename **pp_smb_fname_out)
-{
- char *parent;
- const char *base;
- struct smb_filename *smb_fname = cp_smb_filename(ctx,
- smb_fname_in);
-
- if (smb_fname == NULL) {
- return -1;
- }
-
- /* We need streamname to be NULL */
- TALLOC_FREE(smb_fname->stream_name);
-
- /* And we're replacing base_name. */
- TALLOC_FREE(smb_fname->base_name);
-
- if (!parent_dirname(smb_fname, smb_fname_in->base_name,
- &parent, &base)) {
- TALLOC_FREE(smb_fname);
- return -1;
- }
-
- smb_fname->base_name = talloc_asprintf(smb_fname,
- "%s/._%s", parent, base);
- if (smb_fname->base_name == NULL) {
- TALLOC_FREE(smb_fname);
- return -1;
- }
-
- *pp_smb_fname_out = smb_fname;
-
- return 0;
-}
-
-/**
- * Allocate and initialize an AfpInfo struct
- **/
-static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
-{
- AfpInfo *ai = talloc_zero(ctx, AfpInfo);
- if (ai == NULL) {
- return NULL;
- }
- ai->afpi_Signature = AFP_Signature;
- ai->afpi_Version = AFP_Version;
- ai->afpi_BackupTime = AD_DATE_START;
- return ai;
-}
-
-/**
- * Pack an AfpInfo struct into a buffer
- *
- * Buffer size must be at least AFP_INFO_SIZE
- * Returns size of packed buffer
- **/
-static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
-{
- memset(buf, 0, AFP_INFO_SIZE);
-
- RSIVAL(buf, 0, ai->afpi_Signature);
- RSIVAL(buf, 4, ai->afpi_Version);
- RSIVAL(buf, 12, ai->afpi_BackupTime);
- memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
-
- return AFP_INFO_SIZE;
-}
-
-/**
- * Unpack a buffer into a AfpInfo structure
- *
- * Buffer size must be at least AFP_INFO_SIZE
- * Returns allocated AfpInfo struct
- **/
-static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
-{
- AfpInfo *ai = talloc_zero(ctx, AfpInfo);
- if (ai == NULL) {
- return NULL;
- }
-
- ai->afpi_Signature = RIVAL(data, 0);
- ai->afpi_Version = RIVAL(data, 4);
- ai->afpi_BackupTime = RIVAL(data, 12);
- memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
- sizeof(ai->afpi_FinderInfo));
-
- if (ai->afpi_Signature != AFP_Signature
- || ai->afpi_Version != AFP_Version) {
- DEBUG(1, ("Bad AfpInfo signature or version\n"));
- TALLOC_FREE(ai);
- }
-
- return ai;
-}
-
static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
struct stream_struct **streams,
const char *name, off_t size,
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index 5e0047da917..5dae8a342b6 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -123,7 +123,7 @@ bld.SAMBA3_MODULE('vfs_netatalk',
bld.SAMBA3_MODULE('vfs_fruit',
subsystem='vfs',
source='vfs_fruit.c',
- deps='samba-util OFFLOAD_TOKEN STRING_REPLACE HASH_INODE',
+ deps='samba-util OFFLOAD_TOKEN STRING_REPLACE HASH_INODE ADOUBLE',
init_function='',
internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_fruit'),
enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_fruit'))
diff --git a/source3/wscript_build b/source3/wscript_build
index aa3c7175202..db29a7d2a15 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1081,6 +1081,10 @@ bld.SAMBA3_SUBSYSTEM('tevent-glib-glue',
enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'),
)
+bld.SAMBA3_SUBSYSTEM('ADOUBLE',
+ source='lib/adouble.c',
+ deps='STRING_REPLACE')
+
########################## BINARIES #################################
bld.SAMBA3_BINARY('smbd/smbd',