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"
29 "._" AppleDouble Header File Layout:
35 .-- AD ENTRY[0] Finder Info Entry (must be first)
36 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
38 | '-> FINDER INFO Fixed Size Data (32 Bytes)
39 | ~~~~~~~~~~~~~ 2 Bytes Padding
40 | EXT ATTR HDR Fixed Size Data (36 Bytes)
43 | ATTR ENTRY[1] --+--.
44 | ATTR ENTRY[2] --+--+--.
46 | ATTR ENTRY[N] --+--+--+--.
47 | ATTR DATA 0 <-' | | |
49 | ATTR DATA 1 <----' | |
51 | ATTR DATA 2 <-------' |
54 | ATTR DATA N <----------'
56 | ... Attribute Free Space
59 ... Variable Sized Data
63 /* Number of actually used entries */
64 #define ADEID_NUM_XATTR 8
65 #define ADEID_NUM_DOT_UND 2
66 #define ADEID_NUM_RSRC_XATTR 1
68 /* Sizes of relevant entry bits */
69 #define ADEDLEN_MAGIC 4
70 #define ADEDLEN_VERSION 4
71 #define ADEDLEN_FILLER 16
72 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
73 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
74 #define ADEDLEN_NENTRIES 2
75 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
76 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
77 #define AD_ENTRY_LEN_EID 4
78 #define AD_ENTRY_LEN_OFF 4
79 #define AD_ENTRY_LEN_LEN 4
80 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
83 #define ADEDOFF_MAGIC 0
84 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
85 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
86 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
88 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
89 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
90 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
91 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
92 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
94 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
95 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
96 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
97 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
99 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
100 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
101 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
103 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
104 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
105 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
106 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
107 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
108 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
110 #if AD_DATASZ_XATTR != 402
111 #error bad size for AD_DATASZ_XATTR
114 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
115 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
117 #if AD_DATASZ_DOT_UND != 82
118 #error bad size for AD_DATASZ_DOT_UND
121 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
122 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
123 #define AD_XATTR_HDR_SIZE 36
124 #define AD_XATTR_MAX_HDR_SIZE 65536
127 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
128 * representation as well as the on-disk format.
130 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
131 * the length of the FinderInfo entry is larger then 32 bytes. It is then
132 * preceeded with 2 bytes padding.
134 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
137 struct ad_xattr_header {
138 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
139 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
140 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
141 uint32_t adx_data_start; /* file offset to attribute data area */
142 uint32_t adx_data_length; /* length of attribute data area */
143 uint32_t adx_reserved[3];
145 uint16_t adx_num_attrs;
148 /* On-disk entries are aligned on 4 byte boundaries */
149 struct ad_xattr_entry {
150 uint32_t adx_offset; /* file offset to data */
151 uint32_t adx_length; /* size of attribute data */
153 uint8_t adx_namelen; /* included the NULL terminator */
154 char *adx_name; /* NULL-terminated UTF-8 name */
163 files_struct *ad_fsp;
165 adouble_type_t ad_type;
168 uint8_t ad_filler[ADEDLEN_FILLER];
169 struct ad_entry ad_eid[ADEID_MAX];
171 struct ad_xattr_header adx_header;
172 struct ad_xattr_entry *adx_entries;
175 struct ad_entry_order {
176 uint32_t id, offset, len;
179 /* Netatalk AppleDouble metadata xattr */
181 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
182 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
183 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
184 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
185 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
186 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
187 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
188 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
189 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
193 /* AppleDouble resource fork file (the ones prefixed by "._") */
195 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
196 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
197 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
201 /* Conversion from enumerated id to on-disk AppleDouble id */
202 #define AD_EID_DISK(a) (set_eid[a])
203 static const uint32_t set_eid[] = {
204 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
205 AD_DEV, AD_INO, AD_SYN, AD_ID
208 static char empty_resourcefork[] = {
209 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
211 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
212 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
213 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
214 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
215 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
216 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
247 size_t ad_getentrylen(const struct adouble *ad, int eid)
249 return ad->ad_eid[eid].ade_len;
252 size_t ad_getentryoff(const struct adouble *ad, int eid)
254 return ad->ad_eid[eid].ade_off;
257 size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
259 return ad->ad_eid[eid].ade_len = len;
262 size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
264 return ad->ad_eid[eid].ade_off = off;
268 * Return a pointer to an AppleDouble entry
270 * Returns NULL if the entry is not present
272 char *ad_get_entry(const struct adouble *ad, int eid)
274 off_t off = ad_getentryoff(ad, eid);
275 size_t len = ad_getentrylen(ad, eid);
277 if (off == 0 || len == 0) {
281 return ad->ad_data + off;
287 int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
289 bool xlate = (dateoff & AD_DATE_UNIX);
292 dateoff &= AD_DATE_MASK;
293 p = ad_get_entry(ad, ADEID_FILEDATESI);
298 if (dateoff > AD_DATE_ACCESS) {
302 memcpy(date, p + dateoff, sizeof(uint32_t));
305 *date = AD_DATE_TO_UNIX(*date);
313 int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
315 bool xlate = (dateoff & AD_DATE_UNIX);
318 p = ad_get_entry(ad, ADEID_FILEDATESI);
323 dateoff &= AD_DATE_MASK;
325 date = AD_DATE_FROM_UNIX(date);
328 if (dateoff > AD_DATE_ACCESS) {
332 memcpy(p + dateoff, &date, sizeof(date));
339 * Map on-disk AppleDouble id to enumerated id
341 static uint32_t get_eid(uint32_t eid)
349 return ADEID_PRIVDEV;
351 return ADEID_PRIVINO;
353 return ADEID_PRIVSYN;
364 * Pack AppleDouble structure into data buffer
366 static bool ad_pack(struct vfs_handle_struct *handle,
375 bufsize = talloc_get_size(ad->ad_data);
376 if (bufsize < AD_DATASZ_DOT_UND) {
377 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
381 if (offset + ADEDLEN_MAGIC < offset ||
382 offset + ADEDLEN_MAGIC >= bufsize) {
385 RSIVAL(ad->ad_data, offset, ad->ad_magic);
386 offset += ADEDLEN_MAGIC;
388 if (offset + ADEDLEN_VERSION < offset ||
389 offset + ADEDLEN_VERSION >= bufsize) {
392 RSIVAL(ad->ad_data, offset, ad->ad_version);
393 offset += ADEDLEN_VERSION;
395 if (offset + ADEDLEN_FILLER < offset ||
396 offset + ADEDLEN_FILLER >= bufsize) {
399 if (ad->ad_type == ADOUBLE_RSRC) {
400 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
402 offset += ADEDLEN_FILLER;
404 if (offset + ADEDLEN_NENTRIES < offset ||
405 offset + ADEDLEN_NENTRIES >= bufsize) {
408 offset += ADEDLEN_NENTRIES;
410 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
411 if (ad->ad_eid[eid].ade_off == 0) {
413 * ade_off is also used as indicator whether a
414 * specific entry is used or not
419 if (offset + AD_ENTRY_LEN_EID < offset ||
420 offset + AD_ENTRY_LEN_EID >= bufsize) {
423 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
424 offset += AD_ENTRY_LEN_EID;
426 if (offset + AD_ENTRY_LEN_OFF < offset ||
427 offset + AD_ENTRY_LEN_OFF >= bufsize) {
430 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
431 offset += AD_ENTRY_LEN_OFF;
433 if (offset + AD_ENTRY_LEN_LEN < offset ||
434 offset + AD_ENTRY_LEN_LEN >= bufsize) {
437 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
438 offset += AD_ENTRY_LEN_LEN;
443 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
446 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
451 static bool ad_unpack_xattrs(struct adouble *ad)
453 struct ad_xattr_header *h = &ad->adx_header;
454 const char *p = ad->ad_data;
458 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
462 /* 2 bytes padding */
463 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
465 h->adx_magic = RIVAL(p, hoff + 0);
466 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
467 h->adx_total_size = RIVAL(p, hoff + 8);
468 h->adx_data_start = RIVAL(p, hoff + 12);
469 h->adx_data_length = RIVAL(p, hoff + 16);
470 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
471 h->adx_num_attrs = RSVAL(p, hoff + 34);
473 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
474 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
478 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
479 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
482 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
483 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
487 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
488 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
492 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
493 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
496 if ((h->adx_data_start + h->adx_data_length) >
497 ad->adx_header.adx_total_size)
499 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
503 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
504 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
508 if (h->adx_num_attrs == 0) {
512 ad->adx_entries = talloc_zero_array(
513 ad, struct ad_xattr_entry, h->adx_num_attrs);
514 if (ad->adx_entries == NULL) {
518 hoff += AD_XATTR_HDR_SIZE;
520 for (i = 0; i < h->adx_num_attrs; i++) {
521 struct ad_xattr_entry *e = &ad->adx_entries[i];
523 hoff = (hoff + 3) & ~3;
525 e->adx_offset = RIVAL(p, hoff + 0);
526 e->adx_length = RIVAL(p, hoff + 4);
527 e->adx_flags = RSVAL(p, hoff + 8);
528 e->adx_namelen = *(p + hoff + 10);
530 if (e->adx_offset >= ad->adx_header.adx_total_size) {
531 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
536 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
537 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
542 if ((e->adx_offset + e->adx_length) >
543 ad->adx_header.adx_total_size)
545 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
550 if (e->adx_namelen == 0) {
551 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
555 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
556 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
560 if ((hoff + 11 + e->adx_namelen) >
561 ad->adx_header.adx_data_start)
563 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
568 e->adx_name = talloc_strndup(ad->adx_entries,
571 if (e->adx_name == NULL) {
575 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
576 e->adx_name, e->adx_offset, e->adx_length);
577 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
580 hoff += 11 + e->adx_namelen;
587 * Unpack an AppleDouble blob into a struct adoble
589 static bool ad_unpack(struct adouble *ad, const size_t nentries,
592 size_t bufsize = talloc_get_size(ad->ad_data);
594 uint32_t eid, len, off;
598 * The size of the buffer ad->ad_data is checked when read, so
599 * we wouldn't have to check our own offsets, a few extra
600 * checks won't hurt though. We have to check the offsets we
601 * read from the buffer anyway.
604 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
605 DEBUG(1, ("bad size\n"));
609 ad->ad_magic = RIVAL(ad->ad_data, 0);
610 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
611 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
612 DEBUG(1, ("wrong magic or version\n"));
616 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
618 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
619 if (adentries != nentries) {
620 DEBUG(1, ("invalid number of entries: %zu\n",
625 /* now, read in the entry bits */
626 for (i = 0; i < adentries; i++) {
627 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
629 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
630 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
632 if (!eid || eid >= ADEID_MAX) {
633 DEBUG(1, ("bogus eid %d\n", eid));
638 * All entries other than the resource fork are
639 * expected to be read into the ad_data buffer, so
640 * ensure the specified offset is within that bound
642 if ((off > bufsize) && (eid != ADEID_RFORK)) {
643 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
649 * All entries besides FinderInfo and resource fork
650 * must fit into the buffer. FinderInfo is special as
651 * it may be larger then the default 32 bytes (if it
652 * contains marshalled xattrs), but we will fixup that
653 * in ad_convert(). And the resource fork is never
654 * accessed directly by the ad_data buf (also see
655 * comment above) anyway.
657 if ((eid != ADEID_RFORK) &&
658 (eid != ADEID_FINDERI) &&
659 ((off + len) > bufsize)) {
660 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
666 * That would be obviously broken
668 if (off > filesize) {
669 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
675 * Check for any entry that has its end beyond the
678 if (off + len < off) {
679 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
680 ", len: %" PRIu32 "\n",
685 if (off + len > filesize) {
687 * If this is the resource fork entry, we fix
688 * up the length, for any other entry we bail
691 if (eid != ADEID_RFORK) {
692 DEBUG(1, ("bogus eid %d: off: %" PRIu32
693 ", len: %" PRIu32 "\n",
699 * Fixup the resource fork entry by limiting
700 * the size to entryoffset - filesize.
702 len = filesize - off;
703 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
704 ", len: %" PRIu32 "\n", off, len));
707 ad->ad_eid[eid].ade_off = off;
708 ad->ad_eid[eid].ade_len = len;
711 ok = ad_unpack_xattrs(ad);
719 static bool ad_convert_move_reso(vfs_handle_struct *handle,
721 const struct smb_filename *smb_fname)
729 rforklen = ad_getentrylen(ad, ADEID_RFORK);
734 buf = talloc_size(ad, rforklen);
737 * This allocates a buffer for reading the resource fork data in
738 * one big swoop. Resource forks won't be larger then, say, 64
739 * MB, I swear, so just doing the allocation with the talloc
740 * limit as safeguard seems safe.
742 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
747 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
749 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
751 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
752 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
756 rforkoff = ADEDOFF_RFORK_DOT_UND;
758 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
760 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
761 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
765 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
767 ret = ad_fset(handle, ad, ad->ad_fsp);
769 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
776 static bool ad_convert_xattr(vfs_handle_struct *handle,
778 const struct smb_filename *smb_fname,
779 const char *catia_mappings,
780 bool *converted_xattr)
782 static struct char_mappings **string_replace_cmaps = NULL;
789 *converted_xattr = false;
791 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
795 if (string_replace_cmaps == NULL) {
796 const char **mappings = NULL;
798 mappings = str_list_make_v3_const(
799 talloc_tos(), catia_mappings, NULL);
800 if (mappings == NULL) {
803 string_replace_cmaps = string_replace_init_map(
804 handle->conn->sconn, mappings);
805 TALLOC_FREE(mappings);
808 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
809 struct ad_xattr_entry *e = &ad->adx_entries[i];
810 char *mapped_name = NULL;
812 struct smb_filename *stream_name = NULL;
813 files_struct *fsp = NULL;
816 status = string_replace_allocate(handle->conn,
818 string_replace_cmaps,
821 vfs_translate_to_windows);
822 if (!NT_STATUS_IS_OK(status) &&
823 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
825 DBG_ERR("string_replace_allocate failed\n");
831 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
833 if (mapped_name == NULL) {
838 stream_name = synthetic_smb_fname(talloc_tos(),
839 smb_fname->base_name,
843 TALLOC_FREE(mapped_name);
844 if (stream_name == NULL) {
845 DBG_ERR("synthetic_smb_fname failed\n");
850 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
852 status = SMB_VFS_CREATE_FILE(
853 handle->conn, /* conn */
855 0, /* root_dir_fid */
856 stream_name, /* fname */
857 FILE_GENERIC_WRITE, /* access_mask */
858 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
859 FILE_OPEN_IF, /* create_disposition */
860 0, /* create_options */
861 0, /* file_attributes */
862 INTERNAL_OPEN_ONLY, /* oplock_request */
864 0, /* allocation_size */
865 0, /* private_flags */
870 NULL, NULL); /* create context */
871 TALLOC_FREE(stream_name);
872 if (!NT_STATUS_IS_OK(status)) {
873 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
878 nwritten = SMB_VFS_PWRITE(fsp,
879 ad->ad_data + e->adx_offset,
882 if (nwritten == -1) {
883 DBG_ERR("SMB_VFS_PWRITE failed\n");
885 close_file(NULL, fsp, ERROR_CLOSE);
891 status = close_file(NULL, fsp, NORMAL_CLOSE);
892 if (!NT_STATUS_IS_OK(status)) {
899 ad->adx_header.adx_num_attrs = 0;
900 TALLOC_FREE(ad->adx_entries);
902 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
904 rc = ad_fset(handle, ad, ad->ad_fsp);
906 DBG_ERR("ad_fset on [%s] failed: %s\n",
907 fsp_str_dbg(ad->ad_fsp), strerror(errno));
912 ok = ad_convert_move_reso(handle, ad, smb_fname);
917 *converted_xattr = true;
924 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
926 const struct smb_filename *smb_fname)
931 struct smb_filename *stream_name = NULL;
932 files_struct *fsp = NULL;
939 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
944 p_ad = ad_get_entry(ad, ADEID_FINDERI);
949 ai = afpinfo_new(talloc_tos());
954 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
956 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
957 if (aiblob.data == NULL) {
962 size = afpinfo_pack(ai, (char *)aiblob.data);
964 if (size != AFP_INFO_SIZE) {
968 stream_name = synthetic_smb_fname(talloc_tos(),
969 smb_fname->base_name,
973 if (stream_name == NULL) {
974 data_blob_free(&aiblob);
975 DBG_ERR("synthetic_smb_fname failed\n");
979 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
981 status = SMB_VFS_CREATE_FILE(
982 handle->conn, /* conn */
984 0, /* root_dir_fid */
985 stream_name, /* fname */
986 FILE_GENERIC_WRITE, /* access_mask */
987 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
988 FILE_OPEN_IF, /* create_disposition */
989 0, /* create_options */
990 0, /* file_attributes */
991 INTERNAL_OPEN_ONLY, /* oplock_request */
993 0, /* allocation_size */
994 0, /* private_flags */
999 NULL, NULL); /* create context */
1000 TALLOC_FREE(stream_name);
1001 if (!NT_STATUS_IS_OK(status)) {
1002 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1006 nwritten = SMB_VFS_PWRITE(fsp,
1010 if (nwritten == -1) {
1011 DBG_ERR("SMB_VFS_PWRITE failed\n");
1012 saved_errno = errno;
1013 close_file(NULL, fsp, ERROR_CLOSE);
1014 errno = saved_errno;
1018 status = close_file(NULL, fsp, NORMAL_CLOSE);
1019 if (!NT_STATUS_IS_OK(status)) {
1027 static bool ad_convert_truncate(vfs_handle_struct *handle,
1029 const struct smb_filename *smb_fname)
1034 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1036 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1044 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1049 size_t rforklen = sizeof(empty_resourcefork);
1057 if (!(flags & AD_CONV_WIPE_BLANK)) {
1061 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1065 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1066 if (nread != rforklen) {
1067 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1068 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1072 cmp = memcmp(buf, empty_resourcefork, rforklen);
1077 ad_setentrylen(ad, ADEID_RFORK, 0);
1079 rc = ad_fset(handle, ad, ad->ad_fsp);
1081 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1089 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1091 const struct smb_filename *smb_fname,
1094 struct smb_filename *ad_name = NULL;
1097 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1101 if (!(flags & AD_CONV_DELETE)) {
1105 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1110 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1112 DBG_ERR("Unlinking [%s] failed: %s\n",
1113 smb_fname_str_dbg(ad_name), strerror(errno));
1114 TALLOC_FREE(ad_name);
1118 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1119 TALLOC_FREE(ad_name);
1125 * Convert from Apple's ._ file to Netatalk
1127 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1128 * bytes containing packed xattrs.
1130 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1133 int ad_convert(struct vfs_handle_struct *handle,
1134 const struct smb_filename *smb_fname,
1135 const char *catia_mappings,
1138 struct adouble *ad = NULL;
1140 bool converted_xattr = false;
1144 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1149 ok = ad_convert_xattr(handle,
1159 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1165 if (converted_xattr || blank) {
1166 ok = ad_convert_truncate(handle, ad, smb_fname);
1173 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1175 DBG_ERR("Failed to convert [%s]\n",
1176 smb_fname_str_dbg(smb_fname));
1181 ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
1194 * Read and parse Netatalk AppleDouble metadata xattr
1196 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1198 const struct smb_filename *smb_fname)
1204 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1206 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1207 AFPINFO_EA_NETATALK, ad->ad_data,
1213 if (errno == ENOATTR) {
1219 DEBUG(2, ("error reading meta xattr: %s\n",
1225 if (ealen != AD_DATASZ_XATTR) {
1226 DEBUG(2, ("bad size %zd\n", ealen));
1232 /* Now parse entries */
1233 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1235 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1241 if (!ad_getentryoff(ad, ADEID_FINDERI)
1242 || !ad_getentryoff(ad, ADEID_COMMENT)
1243 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1244 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1245 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1246 || !ad_getentryoff(ad, ADEID_PRIVINO)
1247 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1248 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1249 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1256 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1257 smb_fname->base_name, rc));
1261 if (errno == EINVAL) {
1263 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1265 AFPINFO_EA_NETATALK);
1273 static int ad_open_rsrc(vfs_handle_struct *handle,
1274 const struct smb_filename *smb_fname,
1277 files_struct **_fsp)
1280 struct smb_filename *adp_smb_fname = NULL;
1281 files_struct *fsp = NULL;
1282 uint32_t access_mask;
1283 uint32_t share_access;
1284 uint32_t create_disposition;
1287 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1292 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1294 TALLOC_FREE(adp_smb_fname);
1298 access_mask = FILE_GENERIC_READ;
1299 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1300 create_disposition = FILE_OPEN;
1302 if (flags & O_RDWR) {
1303 access_mask |= FILE_GENERIC_WRITE;
1304 share_access &= ~FILE_SHARE_WRITE;
1307 status = SMB_VFS_CREATE_FILE(
1308 handle->conn, /* conn */
1310 0, /* root_dir_fid */
1315 0, /* create_options */
1316 0, /* file_attributes */
1317 INTERNAL_OPEN_ONLY, /* oplock_request */
1319 0, /* allocation_size */
1320 0, /* private_flags */
1325 NULL, NULL); /* create context */
1326 TALLOC_FREE(adp_smb_fname);
1327 if (!NT_STATUS_IS_OK(status)) {
1328 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1337 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1338 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1339 * for file IO on the ._ file.
1341 static int ad_open(vfs_handle_struct *handle,
1344 const struct smb_filename *smb_fname,
1350 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1351 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1353 if (ad->ad_type == ADOUBLE_META) {
1359 ad->ad_opened = false;
1363 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1367 ad->ad_opened = true;
1369 DBG_DEBUG("Path [%s] type [%s]\n",
1370 smb_fname->base_name,
1371 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1376 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1378 const struct smb_filename *smb_fname)
1386 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
1388 DBG_ERR("fstat [%s] failed: %s\n",
1389 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1394 * AppleDouble file header content and size, two cases:
1396 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1397 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1399 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1401 size = ad->ad_fsp->fsp_name->st.st_ex_size;
1402 if (size > talloc_array_length(ad->ad_data)) {
1403 if (size > AD_XATTR_MAX_HDR_SIZE) {
1404 size = AD_XATTR_MAX_HDR_SIZE;
1406 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1413 len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
1414 if (len != talloc_array_length(ad->ad_data)) {
1415 DBG_NOTICE("%s %s: bad size: %zd\n",
1416 smb_fname->base_name, strerror(errno), len);
1420 /* Now parse entries */
1421 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
1423 DBG_ERR("invalid AppleDouble resource %s\n",
1424 smb_fname->base_name);
1429 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1430 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1431 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1432 DBG_ERR("invalid AppleDouble resource %s\n",
1433 smb_fname->base_name);
1442 * Read and parse resource fork, either ._ AppleDouble file or xattr
1444 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1446 const struct smb_filename *smb_fname)
1448 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1452 * Read and unpack an AppleDouble metadata xattr or resource
1454 static ssize_t ad_read(vfs_handle_struct *handle,
1456 const struct smb_filename *smb_fname)
1458 switch (ad->ad_type) {
1460 return ad_read_meta(handle, ad, smb_fname);
1462 return ad_read_rsrc(handle, ad, smb_fname);
1468 static int adouble_destructor(struct adouble *ad)
1472 if (!ad->ad_opened) {
1476 SMB_ASSERT(ad->ad_fsp != NULL);
1478 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1479 if (!NT_STATUS_IS_OK(status)) {
1480 DBG_ERR("Closing [%s] failed: %s\n",
1481 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1488 * Allocate a struct adouble without initialiing it
1490 * The struct is either hang of the fsp extension context or if fsp is
1493 * @param[in] ctx talloc context
1494 * @param[in] handle vfs handle
1495 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1497 * @return adouble handle
1499 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1500 adouble_type_t type)
1508 adsize = AD_DATASZ_XATTR;
1511 adsize = AD_DATASZ_DOT_UND;
1517 ad = talloc_zero(ctx, struct adouble);
1524 ad->ad_data = talloc_zero_array(ad, char, adsize);
1525 if (ad->ad_data == NULL) {
1532 ad->ad_magic = AD_MAGIC;
1533 ad->ad_version = AD_VERSION;
1535 talloc_set_destructor(ad, adouble_destructor);
1545 * Allocate and initialize a new struct adouble
1547 * @param[in] ctx talloc context
1548 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1550 * @return adouble handle, initialized
1552 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
1555 const struct ad_entry_order *eid;
1556 struct adouble *ad = NULL;
1557 time_t t = time(NULL);
1561 eid = entry_order_meta_xattr;
1564 eid = entry_order_dot_und;
1570 ad = ad_alloc(ctx, type);
1576 ad->ad_eid[eid->id].ade_off = eid->offset;
1577 ad->ad_eid[eid->id].ade_len = eid->len;
1581 /* put something sane in the date fields */
1582 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1583 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1584 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1585 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1593 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1594 vfs_handle_struct *handle,
1596 const struct smb_filename *smb_fname,
1597 adouble_type_t type)
1601 struct adouble *ad = NULL;
1605 smb_fname = fsp->base_fsp->fsp_name;
1608 DEBUG(10, ("ad_get(%s) called for %s\n",
1609 type == ADOUBLE_META ? "meta" : "rsrc",
1610 smb_fname->base_name));
1612 ad = ad_alloc(ctx, type);
1618 /* Try rw first so we can use the fd in ad_convert() */
1621 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1622 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1624 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1627 DBG_DEBUG("ad_open [%s] error [%s]\n",
1628 smb_fname->base_name, strerror(errno));
1633 len = ad_read(handle, ad, smb_fname);
1635 DEBUG(10, ("error reading AppleDouble for %s\n",
1636 smb_fname->base_name));
1642 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1643 type == ADOUBLE_META ? "meta" : "rsrc",
1644 smb_fname->base_name, rc));
1653 * Return AppleDouble data for a file
1655 * @param[in] ctx talloc context
1656 * @param[in] handle vfs handle
1657 * @param[in] smb_fname pathname to file or directory
1658 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1660 * @return talloced struct adouble or NULL on error
1662 struct adouble *ad_get(TALLOC_CTX *ctx,
1663 vfs_handle_struct *handle,
1664 const struct smb_filename *smb_fname,
1665 adouble_type_t type)
1667 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1671 * Return AppleDouble data for a file
1673 * @param[in] ctx talloc context
1674 * @param[in] handle vfs handle
1675 * @param[in] fsp fsp to use for IO
1676 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1678 * @return talloced struct adouble or NULL on error
1680 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1681 files_struct *fsp, adouble_type_t type)
1683 return ad_get_internal(ctx, handle, fsp, NULL, type);
1687 * Set AppleDouble metadata on a file or directory
1689 * @param[in] ad adouble handle
1691 * @param[in] smb_fname pathname to file or directory
1693 * @return status code, 0 means success
1695 int ad_set(vfs_handle_struct *handle,
1697 const struct smb_filename *smb_fname)
1702 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1704 if (ad->ad_type != ADOUBLE_META) {
1705 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1706 smb_fname->base_name);
1710 ok = ad_pack(handle, ad, NULL);
1715 ret = SMB_VFS_SETXATTR(handle->conn,
1717 AFPINFO_EA_NETATALK,
1719 AD_DATASZ_XATTR, 0);
1721 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1727 * Set AppleDouble metadata on a file or directory
1729 * @param[in] ad adouble handle
1730 * @param[in] fsp file handle
1732 * @return status code, 0 means success
1734 int ad_fset(struct vfs_handle_struct *handle,
1742 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
1745 || (fsp->fh == NULL)
1746 || (fsp->fh->fd == -1))
1748 smb_panic("bad fsp");
1751 ok = ad_pack(handle, ad, fsp);
1756 switch (ad->ad_type) {
1758 rc = SMB_VFS_NEXT_SETXATTR(handle,
1760 AFPINFO_EA_NETATALK,
1762 AD_DATASZ_XATTR, 0);
1766 len = SMB_VFS_NEXT_PWRITE(handle,
1769 ad_getentryoff(ad, ADEID_RFORK),
1771 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
1772 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
1782 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
1787 bool is_adouble_file(const char *path)
1789 const char *p = NULL;
1792 p = strrchr(path, '/');
1800 ADOUBLE_NAME_PREFIX,
1801 strlen(ADOUBLE_NAME_PREFIX));
1809 * Prepend "._" to a basename
1810 * Return a new struct smb_filename with stream_name == NULL.
1812 int adouble_path(TALLOC_CTX *ctx,
1813 const struct smb_filename *smb_fname_in,
1814 struct smb_filename **pp_smb_fname_out)
1818 struct smb_filename *smb_fname = cp_smb_filename(ctx,
1821 if (smb_fname == NULL) {
1825 /* We need streamname to be NULL */
1826 TALLOC_FREE(smb_fname->stream_name);
1828 /* And we're replacing base_name. */
1829 TALLOC_FREE(smb_fname->base_name);
1831 SET_STAT_INVALID(smb_fname->st);
1833 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
1835 TALLOC_FREE(smb_fname);
1839 smb_fname->base_name = talloc_asprintf(smb_fname,
1840 "%s/._%s", parent, base);
1841 if (smb_fname->base_name == NULL) {
1842 TALLOC_FREE(smb_fname);
1846 *pp_smb_fname_out = smb_fname;
1852 * Allocate and initialize an AfpInfo struct
1854 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
1856 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
1860 ai->afpi_Signature = AFP_Signature;
1861 ai->afpi_Version = AFP_Version;
1862 ai->afpi_BackupTime = AD_DATE_START;
1867 * Pack an AfpInfo struct into a buffer
1869 * Buffer size must be at least AFP_INFO_SIZE
1870 * Returns size of packed buffer
1872 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
1874 memset(buf, 0, AFP_INFO_SIZE);
1876 RSIVAL(buf, 0, ai->afpi_Signature);
1877 RSIVAL(buf, 4, ai->afpi_Version);
1878 RSIVAL(buf, 12, ai->afpi_BackupTime);
1879 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
1881 return AFP_INFO_SIZE;
1885 * Unpack a buffer into a AfpInfo structure
1887 * Buffer size must be at least AFP_INFO_SIZE
1888 * Returns allocated AfpInfo struct
1890 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
1892 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
1897 ai->afpi_Signature = RIVAL(data, 0);
1898 ai->afpi_Version = RIVAL(data, 4);
1899 ai->afpi_BackupTime = RIVAL(data, 12);
1900 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
1901 sizeof(ai->afpi_FinderInfo));
1903 if (ai->afpi_Signature != AFP_Signature
1904 || ai->afpi_Version != AFP_Version) {
1905 DEBUG(1, ("Bad AfpInfo signature or version\n"));