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 * All entries besides FinderInfo and resource fork must fit into the
274 * buffer. FinderInfo is special as it may be larger then the default 32 bytes
275 * if it contains marshalled xattrs, which we will fixup that in
276 * ad_convert(). The first 32 bytes however must also be part of the buffer.
278 * The resource fork is never accessed directly by the ad_data buf.
280 static bool ad_entry_check_size(uint32_t eid
,
290 [ADEID_DFORK
] = {-1, false, false}, /* not applicable */
291 [ADEID_RFORK
] = {-1, false, false}, /* no limit */
292 [ADEID_NAME
] = {ADEDLEN_NAME
, false, false},
293 [ADEID_COMMENT
] = {ADEDLEN_COMMENT
, false, false},
294 [ADEID_ICONBW
] = {ADEDLEN_ICONBW
, true, false},
295 [ADEID_ICONCOL
] = {ADEDLEN_ICONCOL
, false, false},
296 [ADEID_FILEI
] = {ADEDLEN_FILEI
, true, false},
297 [ADEID_FILEDATESI
] = {ADEDLEN_FILEDATESI
, true, false},
298 [ADEID_FINDERI
] = {ADEDLEN_FINDERI
, false, true},
299 [ADEID_MACFILEI
] = {ADEDLEN_MACFILEI
, true, false},
300 [ADEID_PRODOSFILEI
] = {ADEDLEN_PRODOSFILEI
, true, false},
301 [ADEID_MSDOSFILEI
] = {ADEDLEN_MSDOSFILEI
, true, false},
302 [ADEID_SHORTNAME
] = {ADEDLEN_SHORTNAME
, false, false},
303 [ADEID_AFPFILEI
] = {ADEDLEN_AFPFILEI
, true, false},
304 [ADEID_DID
] = {ADEDLEN_DID
, true, false},
305 [ADEID_PRIVDEV
] = {ADEDLEN_PRIVDEV
, true, false},
306 [ADEID_PRIVINO
] = {ADEDLEN_PRIVINO
, true, false},
307 [ADEID_PRIVSYN
] = {ADEDLEN_PRIVSYN
, true, false},
308 [ADEID_PRIVID
] = {ADEDLEN_PRIVID
, true, false},
311 if (eid
>= ADEID_MAX
) {
315 /* Entry present, but empty, allow */
318 if (ad_checks
[eid
].expected_len
== 0) {
320 * Shouldn't happen: implicitly initialized to zero because
321 * explicit initializer missing.
325 if (ad_checks
[eid
].expected_len
== -1) {
326 /* Unused or no limit */
329 if (ad_checks
[eid
].fixed_size
) {
330 if (ad_checks
[eid
].expected_len
!= got_len
) {
331 /* Wrong size fo fixed size entry. */
335 if (ad_checks
[eid
].minimum_size
) {
336 if (got_len
< ad_checks
[eid
].expected_len
) {
338 * Too small for variable sized entry with
344 if (got_len
> ad_checks
[eid
].expected_len
) {
345 /* Too big for variable sized entry. */
350 if (off
+ got_len
< off
) {
354 if (off
+ got_len
> bufsize
) {
362 * Return a pointer to an AppleDouble entry
364 * Returns NULL if the entry is not present
366 char *ad_get_entry(const struct adouble
*ad
, int eid
)
368 size_t bufsize
= talloc_get_size(ad
->ad_data
);
369 off_t off
= ad_getentryoff(ad
, eid
);
370 size_t len
= ad_getentrylen(ad
, eid
);
373 valid
= ad_entry_check_size(eid
, bufsize
, off
, len
);
378 if (off
== 0 || len
== 0) {
382 return ad
->ad_data
+ off
;
388 int ad_getdate(const struct adouble
*ad
, unsigned int dateoff
, uint32_t *date
)
390 bool xlate
= (dateoff
& AD_DATE_UNIX
);
393 dateoff
&= AD_DATE_MASK
;
394 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
399 if (dateoff
> AD_DATE_ACCESS
) {
403 memcpy(date
, p
+ dateoff
, sizeof(uint32_t));
406 *date
= AD_DATE_TO_UNIX(*date
);
414 int ad_setdate(struct adouble
*ad
, unsigned int dateoff
, uint32_t date
)
416 bool xlate
= (dateoff
& AD_DATE_UNIX
);
419 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
424 dateoff
&= AD_DATE_MASK
;
426 date
= AD_DATE_FROM_UNIX(date
);
429 if (dateoff
> AD_DATE_ACCESS
) {
433 memcpy(p
+ dateoff
, &date
, sizeof(date
));
440 * Map on-disk AppleDouble id to enumerated id
442 static uint32_t get_eid(uint32_t eid
)
450 return ADEID_PRIVDEV
;
452 return ADEID_PRIVINO
;
454 return ADEID_PRIVSYN
;
465 * Move resourcefork data in an AppleDouble file
467 * This is supposed to make room in an AppleDouble file by moving the
468 * resourcefork data behind the space required for packing additional xattr data
469 * in the extended FinderInfo entry.
471 * When we're called we're expecting an AppleDouble file with just two entries
472 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
473 * offset of ADEDOFF_RFORK_DOT_UND.
475 static bool ad_pack_move_reso(struct vfs_handle_struct
*handle
,
484 reso_len
= ad_getentrylen(ad
, ADEID_RFORK
);
485 reso_off
= ad_getentryoff(ad
, ADEID_RFORK
);
491 if (ad
->ad_rsrc_data
== NULL
) {
493 * This buffer is already set when converting a resourcefork
494 * stream from vfs_streams_depot backend via ad_unconvert(). It
495 * is NULL with vfs_streams_xattr where the resourcefork stream
496 * is stored in an AppleDouble sidecar file vy vfs_fruit.
498 ad
->ad_rsrc_data
= talloc_size(ad
, reso_len
);
499 if (ad
->ad_rsrc_data
== NULL
) {
503 n
= SMB_VFS_NEXT_PREAD(handle
,
507 ADEDOFF_RFORK_DOT_UND
);
509 DBG_ERR("Read on [%s] failed\n",
516 n
= SMB_VFS_NEXT_PWRITE(handle
,
522 DBG_ERR("Write on [%s] failed\n",
533 static bool ad_pack_xattrs(struct vfs_handle_struct
*handle
,
537 struct ad_xattr_header
*h
= &ad
->adx_header
;
544 if (ad
->adx_entries
== NULL
) {
545 /* No xattrs, nothing to pack */
550 DBG_ERR("fsp unexpectedly NULL\n");
554 oldsize
= talloc_get_size(ad
->ad_data
);
555 if (oldsize
< AD_XATTR_MAX_HDR_SIZE
) {
556 ad
->ad_data
= talloc_realloc(ad
,
559 AD_XATTR_MAX_HDR_SIZE
);
560 if (ad
->ad_data
== NULL
) {
563 memset(ad
->ad_data
+ oldsize
,
565 AD_XATTR_MAX_HDR_SIZE
- oldsize
);
569 * First, let's calculate the start of the xattr data area which will be
570 * after the xattr header + header entries.
573 data_off
= ad_getentryoff(ad
, ADEID_FINDERI
);
574 data_off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
575 /* 2 bytes padding */
578 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
579 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
581 /* Align on 4 byte boundary */
582 data_off
= (data_off
+ 3) & ~3;
584 data_off
+= e
->adx_namelen
+ ADX_ENTRY_FIXED_SIZE
;
585 if (data_off
>= AD_XATTR_MAX_HDR_SIZE
) {
590 off
= ad_getentryoff(ad
, ADEID_FINDERI
);
591 off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
592 /* 2 bytes padding */
595 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
596 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
598 /* Align on 4 byte boundary */
599 off
= (off
+ 3) & ~3;
601 e
->adx_offset
= data_off
;
602 data_off
+= e
->adx_length
;
604 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
605 "adx_data_off [%zu]\n",
608 (size_t)e
->adx_namelen
,
610 (size_t)e
->adx_length
,
611 (size_t)e
->adx_offset
);
613 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
616 RSIVAL(ad
->ad_data
, off
, e
->adx_offset
);
619 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
622 RSIVAL(ad
->ad_data
, off
, e
->adx_length
);
625 if (off
+ 2 >= AD_XATTR_MAX_HDR_SIZE
) {
628 RSSVAL(ad
->ad_data
, off
, e
->adx_flags
);
631 if (off
+ 1 >= AD_XATTR_MAX_HDR_SIZE
) {
634 SCVAL(ad
->ad_data
, off
, e
->adx_namelen
);
637 if (off
+ e
->adx_namelen
>= AD_XATTR_MAX_HDR_SIZE
) {
640 memcpy(ad
->ad_data
+ off
, e
->adx_name
, e
->adx_namelen
);
641 off
+= e
->adx_namelen
;
644 h
->adx_data_start
= off
;
645 h
->adx_data_length
= talloc_get_size(ad
->adx_data
);
646 h
->adx_total_size
= h
->adx_data_start
+ h
->adx_data_length
;
648 if (talloc_get_size(ad
->ad_data
) < h
->adx_total_size
) {
649 ad
->ad_data
= talloc_realloc(ad
,
653 if (ad
->ad_data
== NULL
) {
658 memcpy(ad
->ad_data
+ h
->adx_data_start
,
664 h
->adx_total_size
- ad_getentryoff(ad
, ADEID_FINDERI
));
668 ad_getentryoff(ad
, ADEID_FINDERI
) +
669 ad_getentrylen(ad
, ADEID_FINDERI
));
671 memcpy(ad
->ad_data
+ ADEDOFF_FILLER
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
674 * Rewind, then update the header fields.
677 off
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
;
678 /* 2 bytes padding */
681 RSIVAL(ad
->ad_data
, off
, AD_XATTR_HDR_MAGIC
);
683 RSIVAL(ad
->ad_data
, off
, 0);
685 RSIVAL(ad
->ad_data
, off
, h
->adx_total_size
);
687 RSIVAL(ad
->ad_data
, off
, h
->adx_data_start
);
689 RSIVAL(ad
->ad_data
, off
, h
->adx_data_length
);
692 /* adx_reserved and adx_flags */
693 memset(ad
->ad_data
+ off
, 0, 3 * 4 + 2);
696 RSSVAL(ad
->ad_data
, off
, h
->adx_num_attrs
);
699 ok
= ad_pack_move_reso(handle
, ad
, fsp
);
701 DBG_ERR("Moving resourcefork of [%s] failed\n",
710 * Pack AppleDouble structure into data buffer
712 static bool ad_pack(struct vfs_handle_struct
*handle
,
722 bufsize
= talloc_get_size(ad
->ad_data
);
723 if (bufsize
< AD_DATASZ_DOT_UND
) {
724 DBG_ERR("bad buffer size [0x%" PRIx32
"]\n", bufsize
);
728 if (offset
+ ADEDLEN_MAGIC
< offset
||
729 offset
+ ADEDLEN_MAGIC
>= bufsize
) {
732 RSIVAL(ad
->ad_data
, offset
, ad
->ad_magic
);
733 offset
+= ADEDLEN_MAGIC
;
735 if (offset
+ ADEDLEN_VERSION
< offset
||
736 offset
+ ADEDLEN_VERSION
>= bufsize
) {
739 RSIVAL(ad
->ad_data
, offset
, ad
->ad_version
);
740 offset
+= ADEDLEN_VERSION
;
742 if (offset
+ ADEDLEN_FILLER
< offset
||
743 offset
+ ADEDLEN_FILLER
>= bufsize
) {
746 if (ad
->ad_type
== ADOUBLE_RSRC
) {
747 memcpy(ad
->ad_data
+ offset
, AD_FILLER_TAG
, ADEDLEN_FILLER
);
749 offset
+= ADEDLEN_FILLER
;
751 if (offset
+ ADEDLEN_NENTRIES
< offset
||
752 offset
+ ADEDLEN_NENTRIES
>= bufsize
) {
755 offset
+= ADEDLEN_NENTRIES
;
757 ok
= ad_pack_xattrs(handle
, ad
, fsp
);
762 for (eid
= 0, nent
= 0; eid
< ADEID_MAX
; eid
++) {
763 if (ad
->ad_eid
[eid
].ade_off
== 0) {
765 * ade_off is also used as indicator whether a
766 * specific entry is used or not
771 if (offset
+ AD_ENTRY_LEN_EID
< offset
||
772 offset
+ AD_ENTRY_LEN_EID
>= bufsize
) {
775 RSIVAL(ad
->ad_data
, offset
, AD_EID_DISK(eid
));
776 offset
+= AD_ENTRY_LEN_EID
;
778 if (offset
+ AD_ENTRY_LEN_OFF
< offset
||
779 offset
+ AD_ENTRY_LEN_OFF
>= bufsize
) {
782 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_off
);
783 offset
+= AD_ENTRY_LEN_OFF
;
785 if (offset
+ AD_ENTRY_LEN_LEN
< offset
||
786 offset
+ AD_ENTRY_LEN_LEN
>= bufsize
) {
789 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_len
);
790 offset
+= AD_ENTRY_LEN_LEN
;
795 if (ADEDOFF_NENTRIES
+ 2 >= bufsize
) {
798 RSSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
, nent
);
803 static bool ad_unpack_xattrs(struct adouble
*ad
)
805 struct ad_xattr_header
*h
= &ad
->adx_header
;
806 size_t bufsize
= talloc_get_size(ad
->ad_data
);
807 const char *p
= ad
->ad_data
;
811 if (ad
->ad_type
!= ADOUBLE_RSRC
) {
815 if (ad_getentrylen(ad
, ADEID_FINDERI
) <= ADEDLEN_FINDERI
) {
820 * Ensure the buffer ad->ad_data was allocated by ad_alloc() for an
821 * ADOUBLE_RSRC type (._ AppleDouble file on-disk).
823 if (bufsize
!= AD_XATTR_MAX_HDR_SIZE
) {
827 /* 2 bytes padding */
828 hoff
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
+ 2;
830 h
->adx_magic
= RIVAL(p
, hoff
+ 0);
831 h
->adx_debug_tag
= RIVAL(p
, hoff
+ 4); /* Not used -> not checked */
832 h
->adx_total_size
= RIVAL(p
, hoff
+ 8);
833 h
->adx_data_start
= RIVAL(p
, hoff
+ 12);
834 h
->adx_data_length
= RIVAL(p
, hoff
+ 16);
835 h
->adx_flags
= RSVAL(p
, hoff
+ 32); /* Not used -> not checked */
836 h
->adx_num_attrs
= RSVAL(p
, hoff
+ 34);
838 if (h
->adx_magic
!= AD_XATTR_HDR_MAGIC
) {
839 DBG_ERR("Bad magic: 0x%" PRIx32
"\n", h
->adx_magic
);
843 if (h
->adx_total_size
> ad_getentryoff(ad
, ADEID_RFORK
)) {
844 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
847 if (h
->adx_total_size
> AD_XATTR_MAX_HDR_SIZE
) {
848 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
852 if (h
->adx_data_start
< (hoff
+ AD_XATTR_HDR_SIZE
)) {
853 DBG_ERR("Bad start: 0x%" PRIx32
"\n", h
->adx_data_start
);
857 if ((h
->adx_data_start
+ h
->adx_data_length
) < h
->adx_data_start
) {
858 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
861 if ((h
->adx_data_start
+ h
->adx_data_length
) >
862 ad
->adx_header
.adx_total_size
)
864 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
868 if (h
->adx_num_attrs
> AD_XATTR_MAX_ENTRIES
) {
869 DBG_ERR("Bad num xattrs: %" PRIu16
"\n", h
->adx_num_attrs
);
873 if (h
->adx_num_attrs
== 0) {
877 ad
->adx_entries
= talloc_zero_array(
878 ad
, struct ad_xattr_entry
, h
->adx_num_attrs
);
879 if (ad
->adx_entries
== NULL
) {
883 hoff
+= AD_XATTR_HDR_SIZE
;
885 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
886 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
888 hoff
= (hoff
+ 3) & ~3;
890 e
->adx_offset
= RIVAL(p
, hoff
+ 0);
891 e
->adx_length
= RIVAL(p
, hoff
+ 4);
892 e
->adx_flags
= RSVAL(p
, hoff
+ 8);
893 e
->adx_namelen
= *(p
+ hoff
+ 10);
895 if (e
->adx_offset
>= ad
->adx_header
.adx_total_size
) {
896 DBG_ERR("Bad adx_offset: %" PRIx32
"\n",
901 if ((e
->adx_offset
+ e
->adx_length
) < e
->adx_offset
) {
902 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
907 if ((e
->adx_offset
+ e
->adx_length
) >
908 ad
->adx_header
.adx_total_size
)
910 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
915 if (e
->adx_namelen
== 0) {
916 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
920 if ((hoff
+ 11 + e
->adx_namelen
) < hoff
+ 11) {
921 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
925 if ((hoff
+ 11 + e
->adx_namelen
) >
926 ad
->adx_header
.adx_data_start
)
928 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
933 e
->adx_name
= talloc_strndup(ad
->adx_entries
,
936 if (e
->adx_name
== NULL
) {
940 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
941 e
->adx_name
, e
->adx_offset
, e
->adx_length
);
942 dump_data(10, (uint8_t *)(ad
->ad_data
+ e
->adx_offset
),
945 hoff
+= 11 + e
->adx_namelen
;
952 * Unpack an AppleDouble blob into a struct adoble
954 static bool ad_unpack(struct adouble
*ad
, const size_t nentries
,
957 size_t bufsize
= talloc_get_size(ad
->ad_data
);
959 uint32_t eid
, len
, off
;
963 * The size of the buffer ad->ad_data is checked when read, so
964 * we wouldn't have to check our own offsets, a few extra
965 * checks won't hurt though. We have to check the offsets we
966 * read from the buffer anyway.
969 if (bufsize
< (AD_HEADER_LEN
+ (AD_ENTRY_LEN
* nentries
))) {
970 DEBUG(1, ("bad size\n"));
974 ad
->ad_magic
= RIVAL(ad
->ad_data
, 0);
975 ad
->ad_version
= RIVAL(ad
->ad_data
, ADEDOFF_VERSION
);
976 if ((ad
->ad_magic
!= AD_MAGIC
) || (ad
->ad_version
!= AD_VERSION
)) {
977 DEBUG(1, ("wrong magic or version\n"));
981 memcpy(ad
->ad_filler
, ad
->ad_data
+ ADEDOFF_FILLER
, ADEDLEN_FILLER
);
983 adentries
= RSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
);
984 if (adentries
!= nentries
) {
985 DEBUG(1, ("invalid number of entries: %zu\n",
990 /* now, read in the entry bits */
991 for (i
= 0; i
< adentries
; i
++) {
992 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
994 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
995 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
997 if (!eid
|| eid
>= ADEID_MAX
) {
998 DEBUG(1, ("bogus eid %d\n", eid
));
1003 * All entries other than the resource fork are
1004 * expected to be read into the ad_data buffer, so
1005 * ensure the specified offset is within that bound
1007 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
1008 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
1013 ok
= ad_entry_check_size(eid
, bufsize
, off
, len
);
1015 DBG_ERR("bogus eid [%"PRIu32
"] bufsize [%zu] "
1016 "off [%"PRIu32
"] len [%"PRIu32
"]\n",
1017 eid
, bufsize
, off
, len
);
1022 * That would be obviously broken
1024 if (off
> filesize
) {
1025 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
1031 * Check for any entry that has its end beyond the
1034 if (off
+ len
< off
) {
1035 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
1036 ", len: %" PRIu32
"\n",
1041 if (off
+ len
> filesize
) {
1043 * If this is the resource fork entry, we fix
1044 * up the length, for any other entry we bail
1047 if (eid
!= ADEID_RFORK
) {
1048 DEBUG(1, ("bogus eid %d: off: %" PRIu32
1049 ", len: %" PRIu32
"\n",
1055 * Fixup the resource fork entry by limiting
1056 * the size to entryoffset - filesize.
1058 len
= filesize
- off
;
1059 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
1060 ", len: %" PRIu32
"\n", off
, len
));
1063 ad
->ad_eid
[eid
].ade_off
= off
;
1064 ad
->ad_eid
[eid
].ade_len
= len
;
1067 if (ad
->ad_type
== ADOUBLE_RSRC
) {
1068 ok
= ad_unpack_xattrs(ad
);
1077 static bool ad_convert_move_reso(vfs_handle_struct
*handle
,
1079 const struct smb_filename
*smb_fname
)
1087 rforklen
= ad_getentrylen(ad
, ADEID_RFORK
);
1088 if (rforklen
== 0) {
1092 buf
= talloc_size(ad
, rforklen
);
1095 * This allocates a buffer for reading the resource fork data in
1096 * one big swoop. Resource forks won't be larger then, say, 64
1097 * MB, I swear, so just doing the allocation with the talloc
1098 * limit as safeguard seems safe.
1100 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1105 rforkoff
= ad_getentryoff(ad
, ADEID_RFORK
);
1107 n
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1108 if (n
!= rforklen
) {
1109 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1110 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1114 rforkoff
= ADEDOFF_RFORK_DOT_UND
;
1116 n
= SMB_VFS_PWRITE(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1117 if (n
!= rforklen
) {
1118 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1119 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1123 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1125 ret
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1127 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1134 static bool ad_convert_xattr(vfs_handle_struct
*handle
,
1136 const struct smb_filename
*smb_fname
,
1137 const char *catia_mappings
,
1138 bool *converted_xattr
)
1140 static struct char_mappings
**string_replace_cmaps
= NULL
;
1142 int saved_errno
= 0;
1147 *converted_xattr
= false;
1149 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1153 if (string_replace_cmaps
== NULL
) {
1154 const char **mappings
= NULL
;
1156 mappings
= str_list_make_v3_const(
1157 talloc_tos(), catia_mappings
, NULL
);
1158 if (mappings
== NULL
) {
1161 string_replace_cmaps
= string_replace_init_map(
1162 handle
->conn
->sconn
, mappings
);
1163 TALLOC_FREE(mappings
);
1166 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1167 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1168 char *mapped_name
= NULL
;
1170 struct smb_filename
*stream_name
= NULL
;
1171 files_struct
*fsp
= NULL
;
1174 status
= string_replace_allocate(handle
->conn
,
1176 string_replace_cmaps
,
1179 vfs_translate_to_windows
);
1180 if (!NT_STATUS_IS_OK(status
) &&
1181 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1183 DBG_ERR("string_replace_allocate failed\n");
1189 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1191 if (mapped_name
== NULL
) {
1196 stream_name
= synthetic_smb_fname(talloc_tos(),
1197 smb_fname
->base_name
,
1202 TALLOC_FREE(mapped_name
);
1203 if (stream_name
== NULL
) {
1204 DBG_ERR("synthetic_smb_fname failed\n");
1209 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1211 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, stream_name
);
1212 if (!NT_STATUS_IS_OK(status
) &&
1213 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
))
1219 status
= SMB_VFS_CREATE_FILE(
1220 handle
->conn
, /* conn */
1223 stream_name
, /* fname */
1224 FILE_GENERIC_WRITE
, /* access_mask */
1225 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1226 FILE_OPEN_IF
, /* create_disposition */
1227 0, /* create_options */
1228 0, /* file_attributes */
1229 INTERNAL_OPEN_ONLY
, /* oplock_request */
1231 0, /* allocation_size */
1232 0, /* private_flags */
1237 NULL
, NULL
); /* create context */
1238 TALLOC_FREE(stream_name
);
1239 if (!NT_STATUS_IS_OK(status
)) {
1240 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1245 nwritten
= SMB_VFS_PWRITE(fsp
,
1246 ad
->ad_data
+ e
->adx_offset
,
1249 if (nwritten
== -1) {
1250 DBG_ERR("SMB_VFS_PWRITE failed\n");
1251 saved_errno
= errno
;
1252 close_file_free(NULL
, &fsp
, ERROR_CLOSE
);
1253 errno
= saved_errno
;
1258 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1259 if (!NT_STATUS_IS_OK(status
)) {
1266 ad
->adx_header
.adx_num_attrs
= 0;
1267 TALLOC_FREE(ad
->adx_entries
);
1269 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1271 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1273 DBG_ERR("ad_fset on [%s] failed: %s\n",
1274 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1279 ok
= ad_convert_move_reso(handle
, ad
, smb_fname
);
1284 *converted_xattr
= true;
1291 static bool ad_convert_finderinfo(vfs_handle_struct
*handle
,
1293 const struct smb_filename
*smb_fname
)
1298 struct smb_filename
*stream_name
= NULL
;
1299 files_struct
*fsp
= NULL
;
1303 int saved_errno
= 0;
1306 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1311 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1316 ai
= afpinfo_new(talloc_tos());
1321 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1323 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1324 if (aiblob
.data
== NULL
) {
1329 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1331 if (size
!= AFP_INFO_SIZE
) {
1335 stream_name
= synthetic_smb_fname(talloc_tos(),
1336 smb_fname
->base_name
,
1341 if (stream_name
== NULL
) {
1342 data_blob_free(&aiblob
);
1343 DBG_ERR("synthetic_smb_fname failed\n");
1347 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1349 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, stream_name
);
1350 if (!NT_STATUS_IS_OK(status
) &&
1351 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
))
1356 status
= SMB_VFS_CREATE_FILE(
1357 handle
->conn
, /* conn */
1360 stream_name
, /* fname */
1361 FILE_GENERIC_WRITE
, /* access_mask */
1362 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1363 FILE_OPEN_IF
, /* create_disposition */
1364 0, /* create_options */
1365 0, /* file_attributes */
1366 INTERNAL_OPEN_ONLY
, /* oplock_request */
1368 0, /* allocation_size */
1369 0, /* private_flags */
1374 NULL
, NULL
); /* create context */
1375 TALLOC_FREE(stream_name
);
1376 if (!NT_STATUS_IS_OK(status
)) {
1377 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1381 nwritten
= SMB_VFS_PWRITE(fsp
,
1385 if (nwritten
== -1) {
1386 DBG_ERR("SMB_VFS_PWRITE failed\n");
1387 saved_errno
= errno
;
1388 close_file_free(NULL
, &fsp
, ERROR_CLOSE
);
1389 errno
= saved_errno
;
1393 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1394 if (!NT_STATUS_IS_OK(status
)) {
1402 static bool ad_convert_truncate(vfs_handle_struct
*handle
,
1404 const struct smb_filename
*smb_fname
)
1409 newlen
= ADEDOFF_RFORK_DOT_UND
+ ad_getentrylen(ad
, ADEID_RFORK
);
1411 rc
= SMB_VFS_FTRUNCATE(ad
->ad_fsp
, newlen
);
1419 static bool ad_convert_blank_rfork(vfs_handle_struct
*handle
,
1424 size_t rforklen
= sizeof(empty_resourcefork
);
1432 if (!(flags
& AD_CONV_WIPE_BLANK
)) {
1436 if (ad_getentrylen(ad
, ADEID_RFORK
) != rforklen
) {
1440 nread
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, ADEDOFF_RFORK_DOT_UND
);
1441 if (nread
!= rforklen
) {
1442 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1443 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1447 cmp
= memcmp(buf
, empty_resourcefork
, rforklen
);
1452 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1454 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1456 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1464 static bool ad_convert_delete_adfile(vfs_handle_struct
*handle
,
1466 const struct smb_filename
*smb_fname
,
1469 struct smb_filename
*parent_fname
= NULL
;
1470 struct smb_filename
*at_fname
= NULL
;
1471 struct smb_filename
*ad_name
= NULL
;
1475 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1479 if (!(flags
& AD_CONV_DELETE
)) {
1483 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1488 status
= parent_pathref(talloc_tos(),
1489 handle
->conn
->cwd_fsp
,
1493 TALLOC_FREE(ad_name
);
1494 if (!NT_STATUS_IS_OK(status
)) {
1498 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
1503 DBG_ERR("Unlinking [%s/%s] failed: %s\n",
1504 smb_fname_str_dbg(parent_fname
),
1505 smb_fname_str_dbg(at_fname
), strerror(errno
));
1506 TALLOC_FREE(parent_fname
);
1510 DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1511 smb_fname_str_dbg(parent_fname
),
1512 smb_fname_str_dbg(at_fname
));
1513 TALLOC_FREE(parent_fname
);
1519 * Convert from Apple's ._ file to Netatalk
1521 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1522 * bytes containing packed xattrs.
1524 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1527 int ad_convert(struct vfs_handle_struct
*handle
,
1528 const struct smb_filename
*smb_fname
,
1529 const char *catia_mappings
,
1532 struct adouble
*ad
= NULL
;
1534 bool converted_xattr
= false;
1538 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
1543 ok
= ad_convert_xattr(handle
,
1553 ok
= ad_convert_blank_rfork(handle
, ad
, flags
, &blank
);
1559 if (converted_xattr
|| blank
) {
1560 ok
= ad_convert_truncate(handle
, ad
, smb_fname
);
1567 ok
= ad_convert_finderinfo(handle
, ad
, smb_fname
);
1569 DBG_ERR("Failed to convert [%s]\n",
1570 smb_fname_str_dbg(smb_fname
));
1575 ok
= ad_convert_delete_adfile(handle
,
1590 static bool ad_unconvert_open_ad(TALLOC_CTX
*mem_ctx
,
1591 struct vfs_handle_struct
*handle
,
1592 struct smb_filename
*smb_fname
,
1593 struct smb_filename
*adpath
,
1594 files_struct
**_fsp
)
1596 files_struct
*fsp
= NULL
;
1600 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, adpath
);
1601 if (!NT_STATUS_IS_OK(status
) &&
1602 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
1606 status
= SMB_VFS_CREATE_FILE(
1611 FILE_READ_DATA
|FILE_WRITE_DATA
,
1612 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1614 0, /* create_options */
1615 0, /* file_attributes */
1618 0, /* allocation_size */
1619 0, /* private_flags */
1624 NULL
, NULL
); /* create context */
1625 if (!NT_STATUS_IS_OK(status
)) {
1626 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1627 smb_fname_str_dbg(adpath
), nt_errstr(status
));
1631 if (fsp
->fsp_name
->st
.st_ex_uid
!= smb_fname
->st
.st_ex_uid
||
1632 fsp
->fsp_name
->st
.st_ex_gid
!= smb_fname
->st
.st_ex_gid
)
1634 ret
= SMB_VFS_FCHOWN(fsp
,
1635 smb_fname
->st
.st_ex_uid
,
1636 smb_fname
->st
.st_ex_gid
);
1638 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1639 fsp_str_dbg(fsp
), nt_errstr(status
));
1640 close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1649 static bool ad_unconvert_get_streams(struct vfs_handle_struct
*handle
,
1650 struct smb_filename
*smb_fname
,
1651 TALLOC_CTX
*mem_ctx
,
1652 unsigned int *num_streams
,
1653 struct stream_struct
**streams
)
1655 files_struct
*fsp
= NULL
;
1658 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, smb_fname
);
1659 if (!NT_STATUS_IS_OK(status
)) {
1663 status
= SMB_VFS_CREATE_FILE(
1664 handle
->conn
, /* conn */
1667 smb_fname
, /* fname */
1668 FILE_READ_ATTRIBUTES
, /* access_mask */
1669 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
1671 FILE_OPEN
, /* create_disposition*/
1672 0, /* create_options */
1673 0, /* file_attributes */
1674 INTERNAL_OPEN_ONLY
, /* oplock_request */
1676 0, /* allocation_size */
1677 0, /* private_flags */
1682 NULL
, NULL
); /* create context */
1683 if (!NT_STATUS_IS_OK(status
)) {
1684 DBG_ERR("Opening [%s] failed: %s\n",
1685 smb_fname_str_dbg(smb_fname
),
1690 status
= vfs_fstreaminfo(fsp
,
1694 if (!NT_STATUS_IS_OK(status
)) {
1695 close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1696 DBG_ERR("streaminfo on [%s] failed: %s\n",
1697 smb_fname_str_dbg(smb_fname
),
1702 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1703 if (!NT_STATUS_IS_OK(status
)) {
1704 DBG_ERR("close_file [%s] failed: %s\n",
1705 smb_fname_str_dbg(smb_fname
),
1713 struct ad_collect_state
{
1715 size_t adx_data_off
;
1716 char *rsrc_data_buf
;
1719 static bool ad_collect_one_stream(struct vfs_handle_struct
*handle
,
1720 struct char_mappings
**cmaps
,
1721 struct smb_filename
*smb_fname
,
1722 const struct stream_struct
*stream
,
1724 struct ad_collect_state
*state
)
1726 struct smb_filename
*sname
= NULL
;
1727 files_struct
*fsp
= NULL
;
1728 struct ad_xattr_entry
*e
= NULL
;
1729 char *mapped_name
= NULL
;
1736 sname
= synthetic_smb_fname(ad
,
1737 smb_fname
->base_name
,
1742 if (sname
== NULL
) {
1746 if (is_ntfs_default_stream_smb_fname(sname
)) {
1751 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname
));
1753 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, sname
);
1754 if (!NT_STATUS_IS_OK(status
)) {
1759 status
= SMB_VFS_CREATE_FILE(
1764 FILE_READ_DATA
|DELETE_ACCESS
,
1767 0, /* create_options */
1768 0, /* file_attributes */
1769 INTERNAL_OPEN_ONLY
, /* oplock_request */
1771 0, /* allocation_size */
1772 0, /* private_flags */
1777 NULL
, NULL
); /* create context */
1778 if (!NT_STATUS_IS_OK(status
)) {
1779 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1780 smb_fname_str_dbg(sname
));
1785 if (is_afpinfo_stream(stream
->name
)) {
1786 char buf
[AFP_INFO_SIZE
];
1788 if (stream
->size
!= AFP_INFO_SIZE
) {
1789 DBG_ERR("Bad size [%zd] on [%s]\n",
1790 (ssize_t
)stream
->size
,
1791 smb_fname_str_dbg(sname
));
1796 nread
= SMB_VFS_PREAD(fsp
, buf
, stream
->size
, 0);
1797 if (nread
!= AFP_INFO_SIZE
) {
1798 DBG_ERR("Bad size [%zd] on [%s]\n",
1799 (ssize_t
)stream
->size
,
1800 smb_fname_str_dbg(sname
));
1805 memcpy(ad
->ad_data
+ ADEDOFF_FINDERI_DOT_UND
,
1806 buf
+ AFP_OFF_FinderInfo
,
1809 ok
= set_delete_on_close(fsp
,
1811 fsp
->conn
->session_info
->security_token
,
1812 fsp
->conn
->session_info
->unix_token
);
1814 DBG_ERR("Deleting [%s] failed\n",
1815 smb_fname_str_dbg(sname
));
1823 if (is_afpresource_stream(stream
->name
)) {
1824 ad
->ad_rsrc_data
= talloc_size(ad
, stream
->size
);
1825 if (ad
->ad_rsrc_data
== NULL
) {
1830 nread
= SMB_VFS_PREAD(fsp
,
1834 if (nread
!= stream
->size
) {
1835 DBG_ERR("Bad size [%zd] on [%s]\n",
1836 (ssize_t
)stream
->size
,
1837 smb_fname_str_dbg(sname
));
1842 ad_setentrylen(ad
, ADEID_RFORK
, stream
->size
);
1844 if (!state
->have_adfile
) {
1846 * We have a resource *stream* but no AppleDouble
1847 * sidecar file, this means the share is configured with
1848 * fruit:resource=stream. So we should delete the
1851 ok
= set_delete_on_close(
1854 fsp
->conn
->session_info
->security_token
,
1855 fsp
->conn
->session_info
->unix_token
);
1857 DBG_ERR("Deleting [%s] failed\n",
1858 smb_fname_str_dbg(sname
));
1867 ad
->adx_entries
= talloc_realloc(ad
,
1869 struct ad_xattr_entry
,
1870 ad
->adx_header
.adx_num_attrs
+ 1);
1871 if (ad
->adx_entries
== NULL
) {
1876 e
= &ad
->adx_entries
[ad
->adx_header
.adx_num_attrs
];
1877 *e
= (struct ad_xattr_entry
) {
1878 .adx_length
= stream
->size
,
1880 e
->adx_name
= talloc_strdup(ad
, stream
->name
+ 1);
1881 if (e
->adx_name
== NULL
) {
1885 p
= strchr(e
->adx_name
, ':');
1890 status
= string_replace_allocate(handle
->conn
,
1895 vfs_translate_to_unix
);
1896 if (!NT_STATUS_IS_OK(status
) &&
1897 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1899 DBG_ERR("string_replace_allocate failed\n");
1904 e
->adx_name
= mapped_name
;
1905 e
->adx_namelen
= strlen(e
->adx_name
) + 1,
1907 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1908 ad
->adx_header
.adx_num_attrs
,
1910 (size_t)e
->adx_length
);
1912 ad
->adx_header
.adx_num_attrs
++;
1914 needed_size
= state
->adx_data_off
+ stream
->size
;
1915 if (needed_size
> talloc_get_size(ad
->adx_data
)) {
1916 ad
->adx_data
= talloc_realloc(ad
,
1920 if (ad
->adx_data
== NULL
) {
1926 nread
= SMB_VFS_PREAD(fsp
,
1927 ad
->adx_data
+ state
->adx_data_off
,
1930 if (nread
!= stream
->size
) {
1931 DBG_ERR("Bad size [%zd] on [%s]\n",
1932 (ssize_t
)stream
->size
,
1933 smb_fname_str_dbg(sname
));
1937 state
->adx_data_off
+= nread
;
1939 ok
= set_delete_on_close(fsp
,
1941 fsp
->conn
->session_info
->security_token
,
1942 fsp
->conn
->session_info
->unix_token
);
1944 DBG_ERR("Deleting [%s] failed\n",
1945 smb_fname_str_dbg(sname
));
1953 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1954 if (!NT_STATUS_IS_OK(status
)) {
1955 DBG_ERR("close_file [%s] failed: %s\n",
1956 smb_fname_str_dbg(smb_fname
),
1966 * Convert filesystem metadata to AppleDouble file
1968 bool ad_unconvert(TALLOC_CTX
*mem_ctx
,
1969 struct vfs_handle_struct
*handle
,
1970 const char *catia_mappings
,
1971 struct smb_filename
*smb_fname
,
1974 static struct char_mappings
**cmaps
= NULL
;
1975 TALLOC_CTX
*frame
= talloc_stackframe();
1976 struct ad_collect_state state
;
1977 struct stream_struct
*streams
= NULL
;
1978 struct smb_filename
*adpath
= NULL
;
1979 struct adouble
*ad
= NULL
;
1980 unsigned int num_streams
= 0;
1981 size_t to_convert
= 0;
1982 bool have_rsrc
= false;
1983 files_struct
*fsp
= NULL
;
1991 if (cmaps
== NULL
) {
1992 const char **mappings
= NULL
;
1994 mappings
= str_list_make_v3_const(
1995 frame
, catia_mappings
, NULL
);
1996 if (mappings
== NULL
) {
2000 cmaps
= string_replace_init_map(mem_ctx
, mappings
);
2001 TALLOC_FREE(mappings
);
2004 ok
= ad_unconvert_get_streams(handle
,
2013 for (i
= 0; i
< num_streams
; i
++) {
2014 if (strcasecmp_m(streams
[i
].name
, "::$DATA") == 0) {
2018 if (is_afpresource_stream(streams
[i
].name
)) {
2023 if (to_convert
== 0) {
2028 state
= (struct ad_collect_state
) {
2032 ret
= adouble_path(frame
, smb_fname
, &adpath
);
2038 ret
= SMB_VFS_STAT(handle
->conn
, adpath
);
2040 state
.have_adfile
= true;
2042 if (errno
!= ENOENT
) {
2046 state
.have_adfile
= false;
2049 if (to_convert
== 1 && have_rsrc
&& state
.have_adfile
) {
2051 * So we have just a single stream, the resource fork stream
2052 * from an AppleDouble file. Fine, that means there's nothing to
2059 ad
= ad_init(frame
, ADOUBLE_RSRC
);
2065 for (i
= 0; i
< num_streams
; i
++) {
2066 ok
= ad_collect_one_stream(handle
,
2077 ok
= ad_unconvert_open_ad(frame
, handle
, smb_fname
, adpath
, &fsp
);
2079 DBG_ERR("Failed to open adfile [%s]\n",
2080 smb_fname_str_dbg(smb_fname
));
2084 ret
= ad_fset(handle
, ad
, fsp
);
2095 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
2096 if (!NT_STATUS_IS_OK(status
)) {
2097 DBG_ERR("close_file_free() [%s] failed: %s\n",
2098 smb_fname_str_dbg(smb_fname
),
2108 * Read and parse Netatalk AppleDouble metadata xattr
2110 static ssize_t
ad_read_meta(vfs_handle_struct
*handle
,
2112 const struct smb_filename
*smb_fname
)
2117 struct files_struct
*fsp
= smb_fname
->fsp
;
2119 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
2121 fsp
= metadata_fsp(fsp
);
2123 ealen
= SMB_VFS_FGETXATTR(fsp
,
2124 AFPINFO_EA_NETATALK
,
2132 if (errno
== ENOATTR
) {
2138 DEBUG(2, ("error reading meta xattr: %s\n",
2144 if (ealen
!= AD_DATASZ_XATTR
) {
2145 DEBUG(2, ("bad size %zd\n", ealen
));
2151 /* Now parse entries */
2152 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
2154 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2160 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
2161 || !ad_getentryoff(ad
, ADEID_COMMENT
)
2162 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
2163 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
2164 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
2165 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
2166 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
2167 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
2168 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2175 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2176 smb_fname
->base_name
, rc
));
2180 if (errno
== EINVAL
) {
2182 (void)SMB_VFS_FREMOVEXATTR(fsp
,
2183 AFPINFO_EA_NETATALK
);
2191 static NTSTATUS
adouble_open_rsrc_fsp(const struct files_struct
*dirfsp
,
2192 const struct smb_filename
*smb_base_fname
,
2195 struct files_struct
**_ad_fsp
)
2198 struct adouble
*ad
= NULL
;
2199 struct smb_filename
*adp_smb_fname
= NULL
;
2200 struct files_struct
*ad_fsp
= NULL
;
2202 struct vfs_open_how how
= { .flags
= in_flags
, .mode
= mode
, };
2204 rc
= adouble_path(talloc_tos(),
2208 return NT_STATUS_NO_MEMORY
;
2211 status
= create_internal_fsp(dirfsp
->conn
,
2214 if (!NT_STATUS_IS_OK(status
)) {
2219 how
.flags
&= ~(O_PATH
);
2221 if (how
.flags
& (O_CREAT
| O_TRUNC
| O_WRONLY
)) {
2222 /* We always need read/write access for the metadata header too */
2223 how
.flags
&= ~(O_WRONLY
);
2224 how
.flags
|= O_RDWR
;
2227 status
= fd_openat(dirfsp
,
2231 if (!NT_STATUS_IS_OK(status
)) {
2232 file_free(NULL
, ad_fsp
);
2236 if (how
.flags
& (O_CREAT
| O_TRUNC
)) {
2237 ad
= ad_init(talloc_tos(), ADOUBLE_RSRC
);
2239 file_free(NULL
, ad_fsp
);
2240 return NT_STATUS_NO_MEMORY
;
2243 rc
= ad_fset(ad_fsp
->conn
->vfs_handles
, ad
, ad_fsp
);
2245 file_free(NULL
, ad_fsp
);
2246 return NT_STATUS_IO_DEVICE_ERROR
;
2252 return NT_STATUS_OK
;
2255 NTSTATUS
adouble_open_from_base_fsp(const struct files_struct
*dirfsp
,
2256 struct files_struct
*base_fsp
,
2257 adouble_type_t type
,
2260 struct files_struct
**_ad_fsp
)
2264 SMB_ASSERT(base_fsp
!= NULL
);
2265 SMB_ASSERT(!fsp_is_alternate_stream(base_fsp
));
2269 return NT_STATUS_INTERNAL_ERROR
;
2271 return adouble_open_rsrc_fsp(dirfsp
,
2278 return NT_STATUS_INTERNAL_ERROR
;
2282 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2283 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2284 * for file IO on the ._ file.
2286 static int ad_open(vfs_handle_struct
*handle
,
2289 const struct smb_filename
*smb_fname
,
2295 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
2296 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2298 if (ad
->ad_type
== ADOUBLE_META
) {
2304 ad
->ad_opened
= false;
2308 status
= adouble_open_rsrc_fsp(handle
->conn
->cwd_fsp
,
2313 if (!NT_STATUS_IS_OK(status
)) {
2314 errno
= map_errno_from_nt_status(status
);
2317 ad
->ad_opened
= true;
2319 DBG_DEBUG("Path [%s] type [%s]\n",
2320 smb_fname
->base_name
,
2321 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2326 static ssize_t
ad_read_rsrc_adouble(vfs_handle_struct
*handle
,
2328 const struct smb_filename
*smb_fname
)
2335 ret
= SMB_VFS_NEXT_FSTAT(handle
, ad
->ad_fsp
, &ad
->ad_fsp
->fsp_name
->st
);
2337 DBG_ERR("fstat [%s] failed: %s\n",
2338 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
2342 to_read
= ad
->ad_fsp
->fsp_name
->st
.st_ex_size
;
2343 if (to_read
> AD_XATTR_MAX_HDR_SIZE
) {
2344 to_read
= AD_XATTR_MAX_HDR_SIZE
;
2347 len
= SMB_VFS_NEXT_PREAD(handle
,
2352 if (len
!= to_read
) {
2353 DBG_NOTICE("%s %s: bad size: %zd\n",
2354 smb_fname
->base_name
, strerror(errno
), len
);
2358 /* Now parse entries */
2361 ad
->ad_fsp
->fsp_name
->st
.st_ex_size
);
2363 DBG_ERR("invalid AppleDouble resource %s\n",
2364 smb_fname
->base_name
);
2369 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
2370 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
2371 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
))
2373 DBG_ERR("invalid AppleDouble resource %s\n",
2374 smb_fname
->base_name
);
2383 * Read and parse resource fork, either ._ AppleDouble file or xattr
2385 static ssize_t
ad_read_rsrc(vfs_handle_struct
*handle
,
2387 const struct smb_filename
*smb_fname
)
2389 return ad_read_rsrc_adouble(handle
, ad
, smb_fname
);
2393 * Read and unpack an AppleDouble metadata xattr or resource
2395 static ssize_t
ad_read(vfs_handle_struct
*handle
,
2397 const struct smb_filename
*smb_fname
)
2399 switch (ad
->ad_type
) {
2401 return ad_read_meta(handle
, ad
, smb_fname
);
2403 return ad_read_rsrc(handle
, ad
, smb_fname
);
2409 static int adouble_destructor(struct adouble
*ad
)
2413 if (!ad
->ad_opened
) {
2417 SMB_ASSERT(ad
->ad_fsp
!= NULL
);
2419 status
= fd_close(ad
->ad_fsp
);
2420 if (!NT_STATUS_IS_OK(status
)) {
2421 DBG_ERR("Closing [%s] failed: %s\n",
2422 fsp_str_dbg(ad
->ad_fsp
), nt_errstr(status
));
2424 file_free(NULL
, ad
->ad_fsp
);
2426 ad
->ad_opened
= false;
2432 * Allocate a struct adouble without initialiing it
2434 * The struct is either hang of the fsp extension context or if fsp is
2437 * @param[in] ctx talloc context
2438 * @param[in] handle vfs handle
2439 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2441 * @return adouble handle
2443 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
,
2444 adouble_type_t type
)
2452 adsize
= AD_DATASZ_XATTR
;
2456 * AppleDouble ._ file case, optimize for fewer (but larger)
2459 * - without xattrs size of the header is exactly
2460 * AD_DATASZ_DOT_UND (82) bytes
2462 * - with embedded xattrs it can be larger, up to
2463 * AD_XATTR_MAX_HDR_SIZE
2465 * Larger headers are not supported, but this is a reasonable
2466 * limit that is also employed by the macOS client.
2468 * We used the largest possible size to be able to read the full
2469 * header with one IO.
2471 adsize
= AD_XATTR_MAX_HDR_SIZE
;
2477 ad
= talloc_zero(ctx
, struct adouble
);
2484 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
2485 if (ad
->ad_data
== NULL
) {
2492 ad
->ad_magic
= AD_MAGIC
;
2493 ad
->ad_version
= AD_VERSION
;
2495 talloc_set_destructor(ad
, adouble_destructor
);
2505 * Allocate and initialize a new struct adouble
2507 * @param[in] ctx talloc context
2508 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2510 * @return adouble handle, initialized
2512 struct adouble
*ad_init(TALLOC_CTX
*ctx
, adouble_type_t type
)
2515 const struct ad_entry_order
*eid
;
2516 struct adouble
*ad
= NULL
;
2517 time_t t
= time(NULL
);
2521 eid
= entry_order_meta_xattr
;
2524 eid
= entry_order_dot_und
;
2530 ad
= ad_alloc(ctx
, type
);
2536 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
2537 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
2541 /* put something sane in the date fields */
2542 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
2543 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
2544 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
2545 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
2553 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
2554 vfs_handle_struct
*handle
,
2556 const struct smb_filename
*smb_fname
,
2557 adouble_type_t type
)
2561 struct adouble
*ad
= NULL
;
2565 if (fsp_is_alternate_stream(fsp
)) {
2566 smb_fname
= fsp
->base_fsp
->fsp_name
;
2568 smb_fname
= fsp
->fsp_name
;
2572 DEBUG(10, ("ad_get(%s) called for %s\n",
2573 type
== ADOUBLE_META
? "meta" : "rsrc",
2574 smb_fname
!= NULL
? smb_fname
->base_name
: "???"));
2576 ad
= ad_alloc(ctx
, type
);
2582 /* Try rw first so we can use the fd in ad_convert() */
2585 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2586 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
2588 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2591 DBG_DEBUG("ad_open [%s] error [%s]\n",
2592 smb_fname
->base_name
, strerror(errno
));
2597 len
= ad_read(handle
, ad
, smb_fname
);
2599 DEBUG(10, ("error reading AppleDouble for %s\n",
2600 smb_fname
->base_name
));
2606 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2607 type
== ADOUBLE_META
? "meta" : "rsrc",
2608 smb_fname
->base_name
, rc
));
2617 * Return AppleDouble data for a file
2619 * @param[in] ctx talloc context
2620 * @param[in] handle vfs handle
2621 * @param[in] smb_fname pathname to file or directory
2622 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2624 * @return talloced struct adouble or NULL on error
2626 struct adouble
*ad_get(TALLOC_CTX
*ctx
,
2627 vfs_handle_struct
*handle
,
2628 const struct smb_filename
*smb_fname
,
2629 adouble_type_t type
)
2631 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
2635 * Return AppleDouble data for a file
2637 * @param[in] ctx talloc context
2638 * @param[in] handle vfs handle
2639 * @param[in] fsp fsp to use for IO
2640 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2642 * @return talloced struct adouble or NULL on error
2644 struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
2645 files_struct
*fsp
, adouble_type_t type
)
2647 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
2651 * Set AppleDouble metadata on a file or directory
2653 * @param[in] ad adouble handle
2654 * @param[in] fsp file handle
2656 * @return status code, 0 means success
2658 int ad_fset(struct vfs_handle_struct
*handle
,
2666 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2668 ok
= ad_pack(handle
, ad
, fsp
);
2673 switch (ad
->ad_type
) {
2675 rc
= SMB_VFS_NEXT_FSETXATTR(handle
,
2676 fsp
->base_fsp
? fsp
->base_fsp
: fsp
,
2677 AFPINFO_EA_NETATALK
,
2679 AD_DATASZ_XATTR
, 0);
2682 len
= SMB_VFS_NEXT_PWRITE(handle
,
2685 ad_getentryoff(ad
, ADEID_RFORK
),
2687 if (len
!= ad_getentryoff(ad
, ADEID_RFORK
)) {
2688 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp
), len
);
2698 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2703 bool is_adouble_file(const char *path
)
2705 const char *p
= NULL
;
2708 p
= strrchr(path
, '/');
2716 ADOUBLE_NAME_PREFIX
,
2717 strlen(ADOUBLE_NAME_PREFIX
));
2725 * Prepend "._" to a basename
2726 * Return a new struct smb_filename with stream_name == NULL.
2728 int adouble_path(TALLOC_CTX
*ctx
,
2729 const struct smb_filename
*smb_fname_in
,
2730 struct smb_filename
**pp_smb_fname_out
)
2734 struct smb_filename
*smb_fname
= NULL
;
2736 smb_fname
= cp_smb_filename_nostream(ctx
, smb_fname_in
);
2737 if (smb_fname
== NULL
) {
2741 /* We're replacing base_name. */
2742 TALLOC_FREE(smb_fname
->base_name
);
2744 SET_STAT_INVALID(smb_fname
->st
);
2746 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2748 TALLOC_FREE(smb_fname
);
2752 if (ISDOT(parent
)) {
2753 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2756 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2757 "%s/._%s", parent
, base
);
2759 if (smb_fname
->base_name
== NULL
) {
2760 TALLOC_FREE(smb_fname
);
2764 *pp_smb_fname_out
= smb_fname
;
2770 * Allocate and initialize an AfpInfo struct
2772 AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2774 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2778 ai
->afpi_Signature
= AFP_Signature
;
2779 ai
->afpi_Version
= AFP_Version
;
2780 ai
->afpi_BackupTime
= AD_DATE_START
;
2785 * Pack an AfpInfo struct into a buffer
2787 * Buffer size must be at least AFP_INFO_SIZE
2788 * Returns size of packed buffer
2790 ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2792 memset(buf
, 0, AFP_INFO_SIZE
);
2794 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2795 RSIVAL(buf
, 4, ai
->afpi_Version
);
2796 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2797 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2799 return AFP_INFO_SIZE
;
2803 * Unpack a buffer into a AfpInfo structure
2805 * Buffer size must be at least AFP_INFO_SIZE
2806 * Returns allocated AfpInfo struct
2808 AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
)
2810 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2815 ai
->afpi_Signature
= RIVAL(data
, 0);
2816 ai
->afpi_Version
= RIVAL(data
, 4);
2817 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2818 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2819 sizeof(ai
->afpi_FinderInfo
));
2821 if (ai
->afpi_Signature
!= AFP_Signature
2822 || ai
->afpi_Version
!= AFP_Version
) {
2823 DEBUG(1, ("Bad AfpInfo signature or version\n"));