2 * Samba AppleDouble helpers
4 * Copyright (C) Ralph Boehme, 2019
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "MacExtensions.h"
23 #include "string_replace.h"
24 #include "smbd/smbd.h"
25 #include "system/filesys.h"
26 #include "libcli/security/security.h"
27 #include "lib/util_macstreams.h"
31 "._" AppleDouble Header File Layout:
37 .-- AD ENTRY[0] Finder Info Entry (must be first)
38 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
40 | '-> FINDER INFO Fixed Size Data (32 Bytes)
41 | ~~~~~~~~~~~~~ 2 Bytes Padding
42 | EXT ATTR HDR Fixed Size Data (36 Bytes)
45 | ATTR ENTRY[1] --+--.
46 | ATTR ENTRY[2] --+--+--.
48 | ATTR ENTRY[N] --+--+--+--.
49 | ATTR DATA 0 <-' | | |
51 | ATTR DATA 1 <----' | |
53 | ATTR DATA 2 <-------' |
56 | ATTR DATA N <----------'
58 | ... Attribute Free Space
61 ... Variable Sized Data
65 /* Number of actually used entries */
66 #define ADEID_NUM_XATTR 8
67 #define ADEID_NUM_DOT_UND 2
68 #define ADEID_NUM_RSRC_XATTR 1
70 /* Sizes of relevant entry bits */
71 #define ADEDLEN_MAGIC 4
72 #define ADEDLEN_VERSION 4
73 #define ADEDLEN_FILLER 16
74 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
75 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
76 #define ADEDLEN_NENTRIES 2
77 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
78 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
79 #define AD_ENTRY_LEN_EID 4
80 #define AD_ENTRY_LEN_OFF 4
81 #define AD_ENTRY_LEN_LEN 4
82 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
85 #define ADEDOFF_MAGIC 0
86 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
87 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
88 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
90 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
91 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
92 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
93 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
94 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
96 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
97 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
98 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
99 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
101 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
102 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
103 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
105 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
106 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
107 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
108 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
109 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
110 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
112 #if AD_DATASZ_XATTR != 402
113 #error bad size for AD_DATASZ_XATTR
116 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
117 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
119 #if AD_DATASZ_DOT_UND != 82
120 #error bad size for AD_DATASZ_DOT_UND
123 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
124 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
125 #define AD_XATTR_HDR_SIZE 36
126 #define AD_XATTR_MAX_HDR_SIZE 65536
127 #define ADX_ENTRY_FIXED_SIZE (4+4+2+1)
130 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131 * representation as well as the on-disk format.
133 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
134 * the length of the FinderInfo entry is larger then 32 bytes. It is then
135 * preceeded with 2 bytes padding.
137 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
140 struct ad_xattr_header {
141 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
142 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
143 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
144 uint32_t adx_data_start; /* file offset to attribute data area */
145 uint32_t adx_data_length; /* length of attribute data area */
146 uint32_t adx_reserved[3];
148 uint16_t adx_num_attrs;
151 /* On-disk entries are aligned on 4 byte boundaries */
152 struct ad_xattr_entry {
153 uint32_t adx_offset; /* file offset to data */
154 uint32_t adx_length; /* size of attribute data */
156 uint8_t adx_namelen; /* included the NULL terminator */
157 char *adx_name; /* NULL-terminated UTF-8 name */
166 files_struct *ad_fsp;
168 adouble_type_t ad_type;
171 uint8_t ad_filler[ADEDLEN_FILLER];
172 struct ad_entry ad_eid[ADEID_MAX];
175 struct ad_xattr_header adx_header;
176 struct ad_xattr_entry *adx_entries;
180 struct ad_entry_order {
181 uint32_t id, offset, len;
184 /* Netatalk AppleDouble metadata xattr */
186 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
187 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
188 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
189 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
190 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
191 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
192 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
193 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
194 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
198 /* AppleDouble resource fork file (the ones prefixed by "._") */
200 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
201 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
202 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
206 /* Conversion from enumerated id to on-disk AppleDouble id */
207 #define AD_EID_DISK(a) (set_eid[a])
208 static const uint32_t set_eid[] = {
209 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
210 AD_DEV, AD_INO, AD_SYN, AD_ID
213 static char empty_resourcefork[] = {
214 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
216 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
217 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
218 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
219 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
220 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
221 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
252 size_t ad_getentrylen(const struct adouble *ad, int eid)
254 return ad->ad_eid[eid].ade_len;
257 size_t ad_getentryoff(const struct adouble *ad, int eid)
259 return ad->ad_eid[eid].ade_off;
262 size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
264 return ad->ad_eid[eid].ade_len = len;
267 size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
269 return ad->ad_eid[eid].ade_off = off;
273 * Return a pointer to an AppleDouble entry
275 * Returns NULL if the entry is not present
277 char *ad_get_entry(const struct adouble *ad, int eid)
279 off_t off = ad_getentryoff(ad, eid);
280 size_t len = ad_getentrylen(ad, eid);
282 if (off == 0 || len == 0) {
286 return ad->ad_data + off;
292 int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
294 bool xlate = (dateoff & AD_DATE_UNIX);
297 dateoff &= AD_DATE_MASK;
298 p = ad_get_entry(ad, ADEID_FILEDATESI);
303 if (dateoff > AD_DATE_ACCESS) {
307 memcpy(date, p + dateoff, sizeof(uint32_t));
310 *date = AD_DATE_TO_UNIX(*date);
318 int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
320 bool xlate = (dateoff & AD_DATE_UNIX);
323 p = ad_get_entry(ad, ADEID_FILEDATESI);
328 dateoff &= AD_DATE_MASK;
330 date = AD_DATE_FROM_UNIX(date);
333 if (dateoff > AD_DATE_ACCESS) {
337 memcpy(p + dateoff, &date, sizeof(date));
344 * Map on-disk AppleDouble id to enumerated id
346 static uint32_t get_eid(uint32_t eid)
354 return ADEID_PRIVDEV;
356 return ADEID_PRIVINO;
358 return ADEID_PRIVSYN;
369 * Move resourcefork data in an AppleDouble file
371 * This is supposed to make room in an AppleDouble file by moving the
372 * resourcefork data behind the space required for packing additional xattr data
373 * in the extended FinderInfo entry.
375 * When we're called we're expecting an AppleDouble file with just two entries
376 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
377 * offset of ADEDOFF_RFORK_DOT_UND.
379 static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
388 reso_len = ad_getentrylen(ad, ADEID_RFORK);
389 reso_off = ad_getentryoff(ad, ADEID_RFORK);
395 if (ad->ad_rsrc_data == NULL) {
397 * This buffer is already set when converting a resourcefork
398 * stream from vfs_streams_depot backend via ad_unconvert(). It
399 * is NULL with vfs_streams_xattr where the resourcefork stream
400 * is stored in an AppleDouble sidecar file vy vfs_fruit.
402 ad->ad_rsrc_data = talloc_size(ad, reso_len);
403 if (ad->ad_rsrc_data == NULL) {
407 n = SMB_VFS_NEXT_PREAD(handle,
411 ADEDOFF_RFORK_DOT_UND);
413 DBG_ERR("Read on [%s] failed\n",
420 n = SMB_VFS_NEXT_PWRITE(handle,
426 DBG_ERR("Write on [%s] failed\n",
437 static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
441 struct ad_xattr_header *h = &ad->adx_header;
448 if (ad->adx_entries == NULL) {
449 /* No xattrs, nothing to pack */
454 DBG_ERR("fsp unexpectedly NULL\n");
458 oldsize = talloc_get_size(ad->ad_data);
459 if (oldsize < AD_XATTR_MAX_HDR_SIZE) {
460 ad->ad_data = talloc_realloc(ad,
463 AD_XATTR_MAX_HDR_SIZE);
464 if (ad->ad_data == NULL) {
467 memset(ad->ad_data + oldsize,
469 AD_XATTR_MAX_HDR_SIZE - oldsize);
473 * First, let's calculate the start of the xattr data area which will be
474 * after the xattr header + header entries.
477 data_off = ad_getentryoff(ad, ADEID_FINDERI);
478 data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
479 /* 2 bytes padding */
482 for (i = 0; i < h->adx_num_attrs; i++) {
483 struct ad_xattr_entry *e = &ad->adx_entries[i];
485 /* Align on 4 byte boundary */
486 data_off = (data_off + 3) & ~3;
488 data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
489 if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
494 off = ad_getentryoff(ad, ADEID_FINDERI);
495 off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
496 /* 2 bytes padding */
499 for (i = 0; i < h->adx_num_attrs; i++) {
500 struct ad_xattr_entry *e = &ad->adx_entries[i];
502 /* Align on 4 byte boundary */
503 off = (off + 3) & ~3;
505 e->adx_offset = data_off;
506 data_off += e->adx_length;
508 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
509 "adx_data_off [%zu]\n",
512 (size_t)e->adx_namelen,
514 (size_t)e->adx_length,
515 (size_t)e->adx_offset);
517 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
520 RSIVAL(ad->ad_data, off, e->adx_offset);
523 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
526 RSIVAL(ad->ad_data, off, e->adx_length);
529 if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
532 RSSVAL(ad->ad_data, off, e->adx_flags);
535 if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
538 SCVAL(ad->ad_data, off, e->adx_namelen);
541 if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
544 memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
545 off += e->adx_namelen;
548 h->adx_data_start = off;
549 h->adx_data_length = talloc_get_size(ad->adx_data);
550 h->adx_total_size = h->adx_data_start + h->adx_data_length;
552 if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
553 ad->ad_data = talloc_realloc(ad,
557 if (ad->ad_data == NULL) {
562 memcpy(ad->ad_data + h->adx_data_start,
568 h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
572 ad_getentryoff(ad, ADEID_FINDERI) +
573 ad_getentrylen(ad, ADEID_FINDERI));
575 memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
578 * Rewind, then update the header fields.
581 off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
582 /* 2 bytes padding */
585 RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
587 RSIVAL(ad->ad_data, off, 0);
589 RSIVAL(ad->ad_data, off, h->adx_total_size);
591 RSIVAL(ad->ad_data, off, h->adx_data_start);
593 RSIVAL(ad->ad_data, off, h->adx_data_length);
596 /* adx_reserved and adx_flags */
597 memset(ad->ad_data + off, 0, 3 * 4 + 2);
600 RSSVAL(ad->ad_data, off, h->adx_num_attrs);
603 ok = ad_pack_move_reso(handle, ad, fsp);
605 DBG_ERR("Moving resourcefork of [%s] failed\n",
614 * Pack AppleDouble structure into data buffer
616 static bool ad_pack(struct vfs_handle_struct *handle,
626 bufsize = talloc_get_size(ad->ad_data);
627 if (bufsize < AD_DATASZ_DOT_UND) {
628 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
632 if (offset + ADEDLEN_MAGIC < offset ||
633 offset + ADEDLEN_MAGIC >= bufsize) {
636 RSIVAL(ad->ad_data, offset, ad->ad_magic);
637 offset += ADEDLEN_MAGIC;
639 if (offset + ADEDLEN_VERSION < offset ||
640 offset + ADEDLEN_VERSION >= bufsize) {
643 RSIVAL(ad->ad_data, offset, ad->ad_version);
644 offset += ADEDLEN_VERSION;
646 if (offset + ADEDLEN_FILLER < offset ||
647 offset + ADEDLEN_FILLER >= bufsize) {
650 if (ad->ad_type == ADOUBLE_RSRC) {
651 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
653 offset += ADEDLEN_FILLER;
655 if (offset + ADEDLEN_NENTRIES < offset ||
656 offset + ADEDLEN_NENTRIES >= bufsize) {
659 offset += ADEDLEN_NENTRIES;
661 ok = ad_pack_xattrs(handle, ad, fsp);
666 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
667 if (ad->ad_eid[eid].ade_off == 0) {
669 * ade_off is also used as indicator whether a
670 * specific entry is used or not
675 if (offset + AD_ENTRY_LEN_EID < offset ||
676 offset + AD_ENTRY_LEN_EID >= bufsize) {
679 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
680 offset += AD_ENTRY_LEN_EID;
682 if (offset + AD_ENTRY_LEN_OFF < offset ||
683 offset + AD_ENTRY_LEN_OFF >= bufsize) {
686 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
687 offset += AD_ENTRY_LEN_OFF;
689 if (offset + AD_ENTRY_LEN_LEN < offset ||
690 offset + AD_ENTRY_LEN_LEN >= bufsize) {
693 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
694 offset += AD_ENTRY_LEN_LEN;
699 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
702 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
707 static bool ad_unpack_xattrs(struct adouble *ad)
709 struct ad_xattr_header *h = &ad->adx_header;
710 const char *p = ad->ad_data;
714 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
718 /* 2 bytes padding */
719 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
721 h->adx_magic = RIVAL(p, hoff + 0);
722 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
723 h->adx_total_size = RIVAL(p, hoff + 8);
724 h->adx_data_start = RIVAL(p, hoff + 12);
725 h->adx_data_length = RIVAL(p, hoff + 16);
726 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
727 h->adx_num_attrs = RSVAL(p, hoff + 34);
729 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
730 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
734 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
735 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
738 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
739 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
743 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
744 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
748 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
749 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
752 if ((h->adx_data_start + h->adx_data_length) >
753 ad->adx_header.adx_total_size)
755 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
759 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
760 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
764 if (h->adx_num_attrs == 0) {
768 ad->adx_entries = talloc_zero_array(
769 ad, struct ad_xattr_entry, h->adx_num_attrs);
770 if (ad->adx_entries == NULL) {
774 hoff += AD_XATTR_HDR_SIZE;
776 for (i = 0; i < h->adx_num_attrs; i++) {
777 struct ad_xattr_entry *e = &ad->adx_entries[i];
779 hoff = (hoff + 3) & ~3;
781 e->adx_offset = RIVAL(p, hoff + 0);
782 e->adx_length = RIVAL(p, hoff + 4);
783 e->adx_flags = RSVAL(p, hoff + 8);
784 e->adx_namelen = *(p + hoff + 10);
786 if (e->adx_offset >= ad->adx_header.adx_total_size) {
787 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
792 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
793 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
798 if ((e->adx_offset + e->adx_length) >
799 ad->adx_header.adx_total_size)
801 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
806 if (e->adx_namelen == 0) {
807 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
811 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
812 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
816 if ((hoff + 11 + e->adx_namelen) >
817 ad->adx_header.adx_data_start)
819 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
824 e->adx_name = talloc_strndup(ad->adx_entries,
827 if (e->adx_name == NULL) {
831 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
832 e->adx_name, e->adx_offset, e->adx_length);
833 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
836 hoff += 11 + e->adx_namelen;
843 * Unpack an AppleDouble blob into a struct adoble
845 static bool ad_unpack(struct adouble *ad, const size_t nentries,
848 size_t bufsize = talloc_get_size(ad->ad_data);
850 uint32_t eid, len, off;
854 * The size of the buffer ad->ad_data is checked when read, so
855 * we wouldn't have to check our own offsets, a few extra
856 * checks won't hurt though. We have to check the offsets we
857 * read from the buffer anyway.
860 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
861 DEBUG(1, ("bad size\n"));
865 ad->ad_magic = RIVAL(ad->ad_data, 0);
866 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
867 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
868 DEBUG(1, ("wrong magic or version\n"));
872 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
874 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
875 if (adentries != nentries) {
876 DEBUG(1, ("invalid number of entries: %zu\n",
881 /* now, read in the entry bits */
882 for (i = 0; i < adentries; i++) {
883 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
885 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
886 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
888 if (!eid || eid >= ADEID_MAX) {
889 DEBUG(1, ("bogus eid %d\n", eid));
894 * All entries other than the resource fork are
895 * expected to be read into the ad_data buffer, so
896 * ensure the specified offset is within that bound
898 if ((off > bufsize) && (eid != ADEID_RFORK)) {
899 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
905 * All entries besides FinderInfo and resource fork
906 * must fit into the buffer. FinderInfo is special as
907 * it may be larger then the default 32 bytes (if it
908 * contains marshalled xattrs), but we will fixup that
909 * in ad_convert(). And the resource fork is never
910 * accessed directly by the ad_data buf (also see
911 * comment above) anyway.
913 if ((eid != ADEID_RFORK) &&
914 (eid != ADEID_FINDERI) &&
915 ((off + len) > bufsize)) {
916 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
922 * That would be obviously broken
924 if (off > filesize) {
925 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
931 * Check for any entry that has its end beyond the
934 if (off + len < off) {
935 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
936 ", len: %" PRIu32 "\n",
941 if (off + len > filesize) {
943 * If this is the resource fork entry, we fix
944 * up the length, for any other entry we bail
947 if (eid != ADEID_RFORK) {
948 DEBUG(1, ("bogus eid %d: off: %" PRIu32
949 ", len: %" PRIu32 "\n",
955 * Fixup the resource fork entry by limiting
956 * the size to entryoffset - filesize.
958 len = filesize - off;
959 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
960 ", len: %" PRIu32 "\n", off, len));
963 ad->ad_eid[eid].ade_off = off;
964 ad->ad_eid[eid].ade_len = len;
967 ok = ad_unpack_xattrs(ad);
975 static bool ad_convert_move_reso(vfs_handle_struct *handle,
977 const struct smb_filename *smb_fname)
985 rforklen = ad_getentrylen(ad, ADEID_RFORK);
990 buf = talloc_size(ad, rforklen);
993 * This allocates a buffer for reading the resource fork data in
994 * one big swoop. Resource forks won't be larger then, say, 64
995 * MB, I swear, so just doing the allocation with the talloc
996 * limit as safeguard seems safe.
998 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1003 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1005 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1006 if (n != rforklen) {
1007 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1008 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1012 rforkoff = ADEDOFF_RFORK_DOT_UND;
1014 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1015 if (n != rforklen) {
1016 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1017 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1021 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1023 ret = ad_fset(handle, ad, ad->ad_fsp);
1025 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1032 static bool ad_convert_xattr(vfs_handle_struct *handle,
1034 const struct smb_filename *smb_fname,
1035 const char *catia_mappings,
1036 bool *converted_xattr)
1038 static struct char_mappings **string_replace_cmaps = NULL;
1040 int saved_errno = 0;
1045 *converted_xattr = false;
1047 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1051 if (string_replace_cmaps == NULL) {
1052 const char **mappings = NULL;
1054 mappings = str_list_make_v3_const(
1055 talloc_tos(), catia_mappings, NULL);
1056 if (mappings == NULL) {
1059 string_replace_cmaps = string_replace_init_map(
1060 handle->conn->sconn, mappings);
1061 TALLOC_FREE(mappings);
1064 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1065 struct ad_xattr_entry *e = &ad->adx_entries[i];
1066 char *mapped_name = NULL;
1068 struct smb_filename *stream_name = NULL;
1069 files_struct *fsp = NULL;
1072 status = string_replace_allocate(handle->conn,
1074 string_replace_cmaps,
1077 vfs_translate_to_windows);
1078 if (!NT_STATUS_IS_OK(status) &&
1079 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1081 DBG_ERR("string_replace_allocate failed\n");
1087 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1089 if (mapped_name == NULL) {
1094 stream_name = synthetic_smb_fname(talloc_tos(),
1095 smb_fname->base_name,
1099 TALLOC_FREE(mapped_name);
1100 if (stream_name == NULL) {
1101 DBG_ERR("synthetic_smb_fname failed\n");
1106 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1108 status = SMB_VFS_CREATE_FILE(
1109 handle->conn, /* conn */
1111 0, /* root_dir_fid */
1112 stream_name, /* fname */
1113 FILE_GENERIC_WRITE, /* access_mask */
1114 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1115 FILE_OPEN_IF, /* create_disposition */
1116 0, /* create_options */
1117 0, /* file_attributes */
1118 INTERNAL_OPEN_ONLY, /* oplock_request */
1120 0, /* allocation_size */
1121 0, /* private_flags */
1126 NULL, NULL); /* create context */
1127 TALLOC_FREE(stream_name);
1128 if (!NT_STATUS_IS_OK(status)) {
1129 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1134 nwritten = SMB_VFS_PWRITE(fsp,
1135 ad->ad_data + e->adx_offset,
1138 if (nwritten == -1) {
1139 DBG_ERR("SMB_VFS_PWRITE failed\n");
1140 saved_errno = errno;
1141 close_file(NULL, fsp, ERROR_CLOSE);
1142 errno = saved_errno;
1147 status = close_file(NULL, fsp, NORMAL_CLOSE);
1148 if (!NT_STATUS_IS_OK(status)) {
1155 ad->adx_header.adx_num_attrs = 0;
1156 TALLOC_FREE(ad->adx_entries);
1158 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1160 rc = ad_fset(handle, ad, ad->ad_fsp);
1162 DBG_ERR("ad_fset on [%s] failed: %s\n",
1163 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1168 ok = ad_convert_move_reso(handle, ad, smb_fname);
1173 *converted_xattr = true;
1180 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1182 const struct smb_filename *smb_fname)
1187 struct smb_filename *stream_name = NULL;
1188 files_struct *fsp = NULL;
1192 int saved_errno = 0;
1195 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1200 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1205 ai = afpinfo_new(talloc_tos());
1210 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1212 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1213 if (aiblob.data == NULL) {
1218 size = afpinfo_pack(ai, (char *)aiblob.data);
1220 if (size != AFP_INFO_SIZE) {
1224 stream_name = synthetic_smb_fname(talloc_tos(),
1225 smb_fname->base_name,
1229 if (stream_name == NULL) {
1230 data_blob_free(&aiblob);
1231 DBG_ERR("synthetic_smb_fname failed\n");
1235 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1237 status = SMB_VFS_CREATE_FILE(
1238 handle->conn, /* conn */
1240 0, /* root_dir_fid */
1241 stream_name, /* fname */
1242 FILE_GENERIC_WRITE, /* access_mask */
1243 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1244 FILE_OPEN_IF, /* create_disposition */
1245 0, /* create_options */
1246 0, /* file_attributes */
1247 INTERNAL_OPEN_ONLY, /* oplock_request */
1249 0, /* allocation_size */
1250 0, /* private_flags */
1255 NULL, NULL); /* create context */
1256 TALLOC_FREE(stream_name);
1257 if (!NT_STATUS_IS_OK(status)) {
1258 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1262 nwritten = SMB_VFS_PWRITE(fsp,
1266 if (nwritten == -1) {
1267 DBG_ERR("SMB_VFS_PWRITE failed\n");
1268 saved_errno = errno;
1269 close_file(NULL, fsp, ERROR_CLOSE);
1270 errno = saved_errno;
1274 status = close_file(NULL, fsp, NORMAL_CLOSE);
1275 if (!NT_STATUS_IS_OK(status)) {
1283 static bool ad_convert_truncate(vfs_handle_struct *handle,
1285 const struct smb_filename *smb_fname)
1290 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1292 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1300 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1305 size_t rforklen = sizeof(empty_resourcefork);
1313 if (!(flags & AD_CONV_WIPE_BLANK)) {
1317 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1321 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1322 if (nread != rforklen) {
1323 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1324 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1328 cmp = memcmp(buf, empty_resourcefork, rforklen);
1333 ad_setentrylen(ad, ADEID_RFORK, 0);
1335 rc = ad_fset(handle, ad, ad->ad_fsp);
1337 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1345 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1347 const struct smb_filename *smb_fname,
1350 struct smb_filename *ad_name = NULL;
1353 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1357 if (!(flags & AD_CONV_DELETE)) {
1361 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1366 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1368 DBG_ERR("Unlinking [%s] failed: %s\n",
1369 smb_fname_str_dbg(ad_name), strerror(errno));
1370 TALLOC_FREE(ad_name);
1374 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1375 TALLOC_FREE(ad_name);
1381 * Convert from Apple's ._ file to Netatalk
1383 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1384 * bytes containing packed xattrs.
1386 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1389 int ad_convert(struct vfs_handle_struct *handle,
1390 const struct smb_filename *smb_fname,
1391 const char *catia_mappings,
1394 struct adouble *ad = NULL;
1396 bool converted_xattr = false;
1400 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1405 ok = ad_convert_xattr(handle,
1415 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1421 if (converted_xattr || blank) {
1422 ok = ad_convert_truncate(handle, ad, smb_fname);
1429 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1431 DBG_ERR("Failed to convert [%s]\n",
1432 smb_fname_str_dbg(smb_fname));
1437 ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
1449 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1450 struct vfs_handle_struct *handle,
1451 struct smb_filename *smb_fname,
1452 struct smb_filename *adpath,
1453 files_struct **_fsp)
1455 files_struct *fsp = NULL;
1459 status = SMB_VFS_CREATE_FILE(
1462 0, /* root_dir_fid */
1464 FILE_READ_DATA|FILE_WRITE_DATA,
1465 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1467 0, /* create_options */
1468 0, /* file_attributes */
1471 0, /* allocation_size */
1472 0, /* private_flags */
1477 NULL, NULL); /* create context */
1478 if (!NT_STATUS_IS_OK(status)) {
1479 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1480 smb_fname_str_dbg(adpath), nt_errstr(status));
1484 if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1485 fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1487 ret = SMB_VFS_FCHOWN(fsp,
1488 smb_fname->st.st_ex_uid,
1489 smb_fname->st.st_ex_gid);
1491 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1492 fsp_str_dbg(fsp), nt_errstr(status));
1493 close_file(NULL, fsp, NORMAL_CLOSE);
1502 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1503 struct smb_filename *smb_fname,
1504 TALLOC_CTX *mem_ctx,
1505 unsigned int *num_streams,
1506 struct stream_struct **streams)
1508 files_struct *fsp = NULL;
1511 status = SMB_VFS_CREATE_FILE(
1512 handle->conn, /* conn */
1514 0, /* root_dir_fid */
1515 smb_fname, /* fname */
1516 FILE_READ_ATTRIBUTES, /* access_mask */
1517 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1519 FILE_OPEN, /* create_disposition*/
1520 0, /* create_options */
1521 0, /* file_attributes */
1522 INTERNAL_OPEN_ONLY, /* oplock_request */
1524 0, /* allocation_size */
1525 0, /* private_flags */
1530 NULL, NULL); /* create context */
1531 if (!NT_STATUS_IS_OK(status)) {
1532 DBG_ERR("Opening [%s] failed: %s\n",
1533 smb_fname_str_dbg(smb_fname),
1538 status = vfs_streaminfo(handle->conn,
1544 if (!NT_STATUS_IS_OK(status)) {
1545 close_file(NULL, fsp, NORMAL_CLOSE);
1546 DBG_ERR("streaminfo on [%s] failed: %s\n",
1547 smb_fname_str_dbg(smb_fname),
1552 status = close_file(NULL, fsp, NORMAL_CLOSE);
1553 if (!NT_STATUS_IS_OK(status)) {
1554 DBG_ERR("close_file [%s] failed: %s\n",
1555 smb_fname_str_dbg(smb_fname),
1563 struct ad_collect_state {
1565 size_t adx_data_off;
1566 char *rsrc_data_buf;
1569 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1570 struct char_mappings **cmaps,
1571 struct smb_filename *smb_fname,
1572 const struct stream_struct *stream,
1574 struct ad_collect_state *state)
1576 struct smb_filename *sname = NULL;
1577 files_struct *fsp = NULL;
1578 struct ad_xattr_entry *e = NULL;
1579 char *mapped_name = NULL;
1587 sname = synthetic_smb_fname(ad,
1588 smb_fname->base_name,
1592 if (sname == NULL) {
1596 if (is_ntfs_default_stream_smb_fname(sname)) {
1601 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1603 ret = SMB_VFS_STAT(handle->conn, sname);
1605 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
1610 status = SMB_VFS_CREATE_FILE(
1613 0, /* root_dir_fid */
1615 FILE_READ_DATA|DELETE_ACCESS,
1618 0, /* create_options */
1619 0, /* file_attributes */
1620 INTERNAL_OPEN_ONLY, /* oplock_request */
1622 0, /* allocation_size */
1623 0, /* private_flags */
1628 NULL, NULL); /* create context */
1629 if (!NT_STATUS_IS_OK(status)) {
1630 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1631 smb_fname_str_dbg(sname));
1636 if (is_afpinfo_stream(stream->name)) {
1637 char buf[AFP_INFO_SIZE];
1639 if (stream->size != AFP_INFO_SIZE) {
1640 DBG_ERR("Bad size [%zd] on [%s]\n",
1641 (ssize_t)stream->size,
1642 smb_fname_str_dbg(sname));
1647 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1648 if (nread != AFP_INFO_SIZE) {
1649 DBG_ERR("Bad size [%zd] on [%s]\n",
1650 (ssize_t)stream->size,
1651 smb_fname_str_dbg(sname));
1656 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1657 buf + AFP_OFF_FinderInfo,
1660 ok = set_delete_on_close(fsp,
1662 fsp->conn->session_info->security_token,
1663 fsp->conn->session_info->unix_token);
1665 DBG_ERR("Deleting [%s] failed\n",
1666 smb_fname_str_dbg(sname));
1674 if (is_afpresource_stream(stream->name)) {
1675 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1676 if (ad->ad_rsrc_data == NULL) {
1681 nread = SMB_VFS_PREAD(fsp,
1685 if (nread != stream->size) {
1686 DBG_ERR("Bad size [%zd] on [%s]\n",
1687 (ssize_t)stream->size,
1688 smb_fname_str_dbg(sname));
1693 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1695 if (!state->have_adfile) {
1697 * We have a resource *stream* but no AppleDouble
1698 * sidecar file, this means the share is configured with
1699 * fruit:resource=stream. So we should delete the
1702 ok = set_delete_on_close(
1705 fsp->conn->session_info->security_token,
1706 fsp->conn->session_info->unix_token);
1708 DBG_ERR("Deleting [%s] failed\n",
1709 smb_fname_str_dbg(sname));
1718 ad->adx_entries = talloc_realloc(ad,
1720 struct ad_xattr_entry,
1721 ad->adx_header.adx_num_attrs + 1);
1722 if (ad->adx_entries == NULL) {
1727 e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1728 *e = (struct ad_xattr_entry) {
1729 .adx_length = stream->size,
1731 e->adx_name = talloc_strdup(ad, stream->name + 1);
1732 if (e->adx_name == NULL) {
1736 p = strchr(e->adx_name, ':');
1741 status = string_replace_allocate(handle->conn,
1746 vfs_translate_to_unix);
1747 if (!NT_STATUS_IS_OK(status) &&
1748 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1750 DBG_ERR("string_replace_allocate failed\n");
1755 e->adx_name = mapped_name;
1756 e->adx_namelen = strlen(e->adx_name) + 1,
1758 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1759 ad->adx_header.adx_num_attrs,
1761 (size_t)e->adx_length);
1763 ad->adx_header.adx_num_attrs++;
1765 needed_size = state->adx_data_off + stream->size;
1766 if (needed_size > talloc_get_size(ad->adx_data)) {
1767 ad->adx_data = talloc_realloc(ad,
1771 if (ad->adx_data == NULL) {
1777 nread = SMB_VFS_PREAD(fsp,
1778 ad->adx_data + state->adx_data_off,
1781 if (nread != stream->size) {
1782 DBG_ERR("Bad size [%zd] on [%s]\n",
1783 (ssize_t)stream->size,
1784 smb_fname_str_dbg(sname));
1788 state->adx_data_off += nread;
1790 ok = set_delete_on_close(fsp,
1792 fsp->conn->session_info->security_token,
1793 fsp->conn->session_info->unix_token);
1795 DBG_ERR("Deleting [%s] failed\n",
1796 smb_fname_str_dbg(sname));
1804 status = close_file(NULL, fsp, NORMAL_CLOSE);
1805 if (!NT_STATUS_IS_OK(status)) {
1806 DBG_ERR("close_file [%s] failed: %s\n",
1807 smb_fname_str_dbg(smb_fname),
1817 * Convert filesystem metadata to AppleDouble file
1819 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1820 struct vfs_handle_struct *handle,
1821 const char *catia_mappings,
1822 struct smb_filename *smb_fname,
1825 static struct char_mappings **cmaps = NULL;
1826 TALLOC_CTX *frame = talloc_stackframe();
1827 struct ad_collect_state state;
1828 struct stream_struct *streams = NULL;
1829 struct smb_filename *adpath = NULL;
1830 struct adouble *ad = NULL;
1831 unsigned int num_streams = 0;
1832 size_t to_convert = 0;
1834 files_struct *fsp = NULL;
1842 if (cmaps == NULL) {
1843 const char **mappings = NULL;
1845 mappings = str_list_make_v3_const(
1846 frame, catia_mappings, NULL);
1847 if (mappings == NULL) {
1851 cmaps = string_replace_init_map(mem_ctx, mappings);
1852 TALLOC_FREE(mappings);
1855 ok = ad_unconvert_get_streams(handle,
1864 for (i = 0; i < num_streams; i++) {
1865 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
1869 if (is_afpresource_stream(streams[i].name)) {
1874 if (to_convert == 0) {
1879 state = (struct ad_collect_state) {
1883 ret = adouble_path(frame, smb_fname, &adpath);
1889 ret = SMB_VFS_STAT(handle->conn, adpath);
1891 state.have_adfile = true;
1893 if (errno != ENOENT) {
1897 state.have_adfile = false;
1900 if (to_convert == 1 && have_rsrc && state.have_adfile) {
1902 * So we have just a single stream, the resource fork stream
1903 * from an AppleDouble file. Fine, that means there's nothing to
1910 ad = ad_init(frame, ADOUBLE_RSRC);
1916 for (i = 0; i < num_streams; i++) {
1917 ok = ad_collect_one_stream(handle,
1928 ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
1930 DBG_ERR("Failed to open adfile [%s]\n",
1931 smb_fname_str_dbg(smb_fname));
1935 ret = ad_fset(handle, ad, fsp);
1946 status = close_file(NULL, fsp, NORMAL_CLOSE);
1947 if (!NT_STATUS_IS_OK(status)) {
1948 DBG_ERR("close_file [%s] failed: %s\n",
1949 smb_fname_str_dbg(smb_fname),
1959 * Read and parse Netatalk AppleDouble metadata xattr
1961 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1963 const struct smb_filename *smb_fname)
1969 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1971 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1972 AFPINFO_EA_NETATALK, ad->ad_data,
1978 if (errno == ENOATTR) {
1984 DEBUG(2, ("error reading meta xattr: %s\n",
1990 if (ealen != AD_DATASZ_XATTR) {
1991 DEBUG(2, ("bad size %zd\n", ealen));
1997 /* Now parse entries */
1998 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2000 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2006 if (!ad_getentryoff(ad, ADEID_FINDERI)
2007 || !ad_getentryoff(ad, ADEID_COMMENT)
2008 || !ad_getentryoff(ad, ADEID_FILEDATESI)
2009 || !ad_getentryoff(ad, ADEID_AFPFILEI)
2010 || !ad_getentryoff(ad, ADEID_PRIVDEV)
2011 || !ad_getentryoff(ad, ADEID_PRIVINO)
2012 || !ad_getentryoff(ad, ADEID_PRIVSYN)
2013 || !ad_getentryoff(ad, ADEID_PRIVID)) {
2014 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2021 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2022 smb_fname->base_name, rc));
2026 if (errno == EINVAL) {
2028 (void)SMB_VFS_REMOVEXATTR(handle->conn,
2030 AFPINFO_EA_NETATALK);
2038 static int ad_open_rsrc(vfs_handle_struct *handle,
2039 const struct smb_filename *smb_fname,
2042 files_struct **_fsp)
2045 struct smb_filename *adp_smb_fname = NULL;
2046 files_struct *fsp = NULL;
2047 uint32_t access_mask;
2048 uint32_t share_access;
2049 uint32_t create_disposition;
2052 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2057 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
2059 TALLOC_FREE(adp_smb_fname);
2063 access_mask = FILE_GENERIC_READ;
2064 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
2065 create_disposition = FILE_OPEN;
2067 if (flags & O_RDWR) {
2068 access_mask |= FILE_GENERIC_WRITE;
2069 share_access &= ~FILE_SHARE_WRITE;
2072 status = SMB_VFS_CREATE_FILE(
2073 handle->conn, /* conn */
2075 0, /* root_dir_fid */
2080 0, /* create_options */
2081 0, /* file_attributes */
2082 INTERNAL_OPEN_ONLY, /* oplock_request */
2084 0, /* allocation_size */
2085 0, /* private_flags */
2090 NULL, NULL); /* create context */
2091 TALLOC_FREE(adp_smb_fname);
2092 if (!NT_STATUS_IS_OK(status)) {
2093 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2102 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2103 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2104 * for file IO on the ._ file.
2106 static int ad_open(vfs_handle_struct *handle,
2109 const struct smb_filename *smb_fname,
2115 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2116 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2118 if (ad->ad_type == ADOUBLE_META) {
2124 ad->ad_opened = false;
2128 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
2132 ad->ad_opened = true;
2134 DBG_DEBUG("Path [%s] type [%s]\n",
2135 smb_fname->base_name,
2136 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2141 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2143 const struct smb_filename *smb_fname)
2151 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2153 DBG_ERR("fstat [%s] failed: %s\n",
2154 fsp_str_dbg(ad->ad_fsp), strerror(errno));
2159 * AppleDouble file header content and size, two cases:
2161 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
2162 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
2164 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
2166 size = ad->ad_fsp->fsp_name->st.st_ex_size;
2167 if (size > talloc_array_length(ad->ad_data)) {
2168 if (size > AD_XATTR_MAX_HDR_SIZE) {
2169 size = AD_XATTR_MAX_HDR_SIZE;
2171 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
2178 len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
2179 if (len != talloc_array_length(ad->ad_data)) {
2180 DBG_NOTICE("%s %s: bad size: %zd\n",
2181 smb_fname->base_name, strerror(errno), len);
2185 /* Now parse entries */
2186 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
2188 DBG_ERR("invalid AppleDouble resource %s\n",
2189 smb_fname->base_name);
2194 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2195 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2196 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
2197 DBG_ERR("invalid AppleDouble resource %s\n",
2198 smb_fname->base_name);
2207 * Read and parse resource fork, either ._ AppleDouble file or xattr
2209 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2211 const struct smb_filename *smb_fname)
2213 return ad_read_rsrc_adouble(handle, ad, smb_fname);
2217 * Read and unpack an AppleDouble metadata xattr or resource
2219 static ssize_t ad_read(vfs_handle_struct *handle,
2221 const struct smb_filename *smb_fname)
2223 switch (ad->ad_type) {
2225 return ad_read_meta(handle, ad, smb_fname);
2227 return ad_read_rsrc(handle, ad, smb_fname);
2233 static int adouble_destructor(struct adouble *ad)
2237 if (!ad->ad_opened) {
2241 SMB_ASSERT(ad->ad_fsp != NULL);
2243 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
2244 if (!NT_STATUS_IS_OK(status)) {
2245 DBG_ERR("Closing [%s] failed: %s\n",
2246 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2253 * Allocate a struct adouble without initialiing it
2255 * The struct is either hang of the fsp extension context or if fsp is
2258 * @param[in] ctx talloc context
2259 * @param[in] handle vfs handle
2260 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2262 * @return adouble handle
2264 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2265 adouble_type_t type)
2273 adsize = AD_DATASZ_XATTR;
2276 adsize = AD_DATASZ_DOT_UND;
2282 ad = talloc_zero(ctx, struct adouble);
2289 ad->ad_data = talloc_zero_array(ad, char, adsize);
2290 if (ad->ad_data == NULL) {
2297 ad->ad_magic = AD_MAGIC;
2298 ad->ad_version = AD_VERSION;
2300 talloc_set_destructor(ad, adouble_destructor);
2310 * Allocate and initialize a new struct adouble
2312 * @param[in] ctx talloc context
2313 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2315 * @return adouble handle, initialized
2317 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2320 const struct ad_entry_order *eid;
2321 struct adouble *ad = NULL;
2322 time_t t = time(NULL);
2326 eid = entry_order_meta_xattr;
2329 eid = entry_order_dot_und;
2335 ad = ad_alloc(ctx, type);
2341 ad->ad_eid[eid->id].ade_off = eid->offset;
2342 ad->ad_eid[eid->id].ade_len = eid->len;
2346 /* put something sane in the date fields */
2347 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2348 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2349 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2350 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2358 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2359 vfs_handle_struct *handle,
2361 const struct smb_filename *smb_fname,
2362 adouble_type_t type)
2366 struct adouble *ad = NULL;
2370 smb_fname = fsp->base_fsp->fsp_name;
2373 DEBUG(10, ("ad_get(%s) called for %s\n",
2374 type == ADOUBLE_META ? "meta" : "rsrc",
2375 smb_fname->base_name));
2377 ad = ad_alloc(ctx, type);
2383 /* Try rw first so we can use the fd in ad_convert() */
2386 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2387 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2389 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2392 DBG_DEBUG("ad_open [%s] error [%s]\n",
2393 smb_fname->base_name, strerror(errno));
2398 len = ad_read(handle, ad, smb_fname);
2400 DEBUG(10, ("error reading AppleDouble for %s\n",
2401 smb_fname->base_name));
2407 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2408 type == ADOUBLE_META ? "meta" : "rsrc",
2409 smb_fname->base_name, rc));
2418 * Return AppleDouble data for a file
2420 * @param[in] ctx talloc context
2421 * @param[in] handle vfs handle
2422 * @param[in] smb_fname pathname to file or directory
2423 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2425 * @return talloced struct adouble or NULL on error
2427 struct adouble *ad_get(TALLOC_CTX *ctx,
2428 vfs_handle_struct *handle,
2429 const struct smb_filename *smb_fname,
2430 adouble_type_t type)
2432 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2436 * Return AppleDouble data for a file
2438 * @param[in] ctx talloc context
2439 * @param[in] handle vfs handle
2440 * @param[in] fsp fsp to use for IO
2441 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2443 * @return talloced struct adouble or NULL on error
2445 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2446 files_struct *fsp, adouble_type_t type)
2448 return ad_get_internal(ctx, handle, fsp, NULL, type);
2452 * Set AppleDouble metadata on a file or directory
2454 * @param[in] ad adouble handle
2456 * @param[in] smb_fname pathname to file or directory
2458 * @return status code, 0 means success
2460 int ad_set(vfs_handle_struct *handle,
2462 const struct smb_filename *smb_fname)
2467 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2469 if (ad->ad_type != ADOUBLE_META) {
2470 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2471 smb_fname->base_name);
2475 ok = ad_pack(handle, ad, NULL);
2480 ret = SMB_VFS_SETXATTR(handle->conn,
2482 AFPINFO_EA_NETATALK,
2484 AD_DATASZ_XATTR, 0);
2486 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2492 * Set AppleDouble metadata on a file or directory
2494 * @param[in] ad adouble handle
2495 * @param[in] fsp file handle
2497 * @return status code, 0 means success
2499 int ad_fset(struct vfs_handle_struct *handle,
2507 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2510 || (fsp->fh == NULL)
2511 || (fsp->fh->fd == -1))
2513 smb_panic("bad fsp");
2516 ok = ad_pack(handle, ad, fsp);
2521 switch (ad->ad_type) {
2523 rc = SMB_VFS_NEXT_SETXATTR(handle,
2525 AFPINFO_EA_NETATALK,
2527 AD_DATASZ_XATTR, 0);
2531 len = SMB_VFS_NEXT_PWRITE(handle,
2534 ad_getentryoff(ad, ADEID_RFORK),
2536 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2537 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2547 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2552 bool is_adouble_file(const char *path)
2554 const char *p = NULL;
2557 p = strrchr(path, '/');
2565 ADOUBLE_NAME_PREFIX,
2566 strlen(ADOUBLE_NAME_PREFIX));
2574 * Prepend "._" to a basename
2575 * Return a new struct smb_filename with stream_name == NULL.
2577 int adouble_path(TALLOC_CTX *ctx,
2578 const struct smb_filename *smb_fname_in,
2579 struct smb_filename **pp_smb_fname_out)
2583 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2586 if (smb_fname == NULL) {
2590 /* We need streamname to be NULL */
2591 TALLOC_FREE(smb_fname->stream_name);
2593 /* And we're replacing base_name. */
2594 TALLOC_FREE(smb_fname->base_name);
2596 SET_STAT_INVALID(smb_fname->st);
2598 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2600 TALLOC_FREE(smb_fname);
2604 smb_fname->base_name = talloc_asprintf(smb_fname,
2605 "%s/._%s", parent, base);
2606 if (smb_fname->base_name == NULL) {
2607 TALLOC_FREE(smb_fname);
2611 *pp_smb_fname_out = smb_fname;
2617 * Allocate and initialize an AfpInfo struct
2619 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2621 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2625 ai->afpi_Signature = AFP_Signature;
2626 ai->afpi_Version = AFP_Version;
2627 ai->afpi_BackupTime = AD_DATE_START;
2632 * Pack an AfpInfo struct into a buffer
2634 * Buffer size must be at least AFP_INFO_SIZE
2635 * Returns size of packed buffer
2637 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2639 memset(buf, 0, AFP_INFO_SIZE);
2641 RSIVAL(buf, 0, ai->afpi_Signature);
2642 RSIVAL(buf, 4, ai->afpi_Version);
2643 RSIVAL(buf, 12, ai->afpi_BackupTime);
2644 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2646 return AFP_INFO_SIZE;
2650 * Unpack a buffer into a AfpInfo structure
2652 * Buffer size must be at least AFP_INFO_SIZE
2653 * Returns allocated AfpInfo struct
2655 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2657 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2662 ai->afpi_Signature = RIVAL(data, 0);
2663 ai->afpi_Version = RIVAL(data, 4);
2664 ai->afpi_BackupTime = RIVAL(data, 12);
2665 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2666 sizeof(ai->afpi_FinderInfo));
2668 if (ai->afpi_Signature != AFP_Signature
2669 || ai->afpi_Version != AFP_Version) {
2670 DEBUG(1, ("Bad AfpInfo signature or version\n"));