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 * preceded 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 const 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 for 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 DBG_NOTICE("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 DBG_NOTICE("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 DBG_NOTICE("Invalid number of entries: %zu\n", adentries
);
989 /* now, read in the entry bits */
990 for (i
= 0; i
< adentries
; i
++) {
991 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
993 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
994 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
996 if (!eid
|| eid
>= ADEID_MAX
) {
997 DBG_NOTICE("Bogus eid %d\n", eid
);
1002 * All entries other than the resource fork are
1003 * expected to be read into the ad_data buffer, so
1004 * ensure the specified offset is within that bound
1006 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
1007 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1008 ", len: %" PRIu32
"\n",
1015 ok
= ad_entry_check_size(eid
, bufsize
, off
, len
);
1017 DBG_NOTICE("Bogus eid [%" PRIu32
"] bufsize [%zu] "
1018 "off [%" PRIu32
"] len [%" PRIu32
"]\n",
1027 * That would be obviously broken
1029 if (off
> filesize
) {
1030 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1031 ", len: %" PRIu32
"\n",
1039 * Check for any entry that has its end beyond the
1042 if (off
+ len
< off
) {
1043 DBG_NOTICE("offset wrap in eid %d: off: %" PRIu32
1044 ", len: %" PRIu32
"\n",
1051 if (off
+ len
> filesize
) {
1053 * If this is the resource fork entry, we fix
1054 * up the length, for any other entry we bail
1057 if (eid
!= ADEID_RFORK
) {
1058 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1059 ", len: %" PRIu32
"\n",
1067 * Fixup the resource fork entry by limiting
1068 * the size to entryoffset - filesize.
1070 len
= filesize
- off
;
1071 DBG_NOTICE("Limiting ADEID_RFORK: off: %" PRIu32
1072 ", len: %" PRIu32
"\n",
1077 ad
->ad_eid
[eid
].ade_off
= off
;
1078 ad
->ad_eid
[eid
].ade_len
= len
;
1081 if (ad
->ad_type
== ADOUBLE_RSRC
) {
1082 ok
= ad_unpack_xattrs(ad
);
1091 static bool ad_convert_move_reso(vfs_handle_struct
*handle
,
1093 const struct smb_filename
*smb_fname
)
1101 rforklen
= ad_getentrylen(ad
, ADEID_RFORK
);
1102 if (rforklen
== 0) {
1106 buf
= talloc_size(ad
, rforklen
);
1109 * This allocates a buffer for reading the resource fork data in
1110 * one big swoop. Resource forks won't be larger then, say, 64
1111 * MB, I swear, so just doing the allocation with the talloc
1112 * limit as safeguard seems safe.
1114 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1119 rforkoff
= ad_getentryoff(ad
, ADEID_RFORK
);
1121 n
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1122 if (n
!= rforklen
) {
1123 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1124 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1128 rforkoff
= ADEDOFF_RFORK_DOT_UND
;
1130 n
= SMB_VFS_PWRITE(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1131 if (n
!= rforklen
) {
1132 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1133 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1137 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1139 ret
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1141 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1148 static bool ad_convert_xattr(vfs_handle_struct
*handle
,
1150 const struct smb_filename
*smb_fname
,
1151 const char *catia_mappings
,
1152 bool *converted_xattr
)
1154 static struct char_mappings
**string_replace_cmaps
= NULL
;
1156 int saved_errno
= 0;
1161 *converted_xattr
= false;
1163 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1167 if (string_replace_cmaps
== NULL
) {
1168 const char **mappings
= NULL
;
1170 mappings
= str_list_make_v3_const(
1171 talloc_tos(), catia_mappings
, NULL
);
1172 if (mappings
== NULL
) {
1175 string_replace_cmaps
= string_replace_init_map(
1176 handle
->conn
->sconn
, mappings
);
1177 TALLOC_FREE(mappings
);
1180 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1181 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1182 char *mapped_name
= NULL
;
1184 struct smb_filename
*stream_name
= NULL
;
1185 files_struct
*fsp
= NULL
;
1188 status
= string_replace_allocate(handle
->conn
,
1190 string_replace_cmaps
,
1193 vfs_translate_to_windows
);
1194 if (!NT_STATUS_IS_OK(status
) &&
1195 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1197 DBG_ERR("string_replace_allocate failed\n");
1203 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1205 if (mapped_name
== NULL
) {
1210 stream_name
= synthetic_smb_fname(talloc_tos(),
1211 smb_fname
->base_name
,
1216 TALLOC_FREE(mapped_name
);
1217 if (stream_name
== NULL
) {
1218 DBG_ERR("synthetic_smb_fname failed\n");
1223 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1225 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, stream_name
);
1226 if (!NT_STATUS_IS_OK(status
) &&
1227 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
))
1233 status
= SMB_VFS_CREATE_FILE(
1234 handle
->conn
, /* conn */
1237 stream_name
, /* fname */
1238 FILE_GENERIC_WRITE
, /* access_mask */
1239 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, /* share_access */
1240 FILE_OPEN_IF
, /* create_disposition */
1241 0, /* create_options */
1242 0, /* file_attributes */
1243 INTERNAL_OPEN_ONLY
, /* oplock_request */
1245 0, /* allocation_size */
1246 0, /* private_flags */
1251 NULL
, NULL
); /* create context */
1252 TALLOC_FREE(stream_name
);
1253 if (!NT_STATUS_IS_OK(status
)) {
1254 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1259 nwritten
= SMB_VFS_PWRITE(fsp
,
1260 ad
->ad_data
+ e
->adx_offset
,
1263 if (nwritten
== -1) {
1264 DBG_ERR("SMB_VFS_PWRITE failed\n");
1265 saved_errno
= errno
;
1266 close_file_free(NULL
, &fsp
, ERROR_CLOSE
);
1267 errno
= saved_errno
;
1272 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1273 if (!NT_STATUS_IS_OK(status
)) {
1280 ad
->adx_header
.adx_num_attrs
= 0;
1281 TALLOC_FREE(ad
->adx_entries
);
1283 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1285 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1287 DBG_ERR("ad_fset on [%s] failed: %s\n",
1288 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1293 ok
= ad_convert_move_reso(handle
, ad
, smb_fname
);
1298 *converted_xattr
= true;
1305 static bool ad_convert_finderinfo(vfs_handle_struct
*handle
,
1307 const struct smb_filename
*smb_fname
)
1312 struct smb_filename
*stream_name
= NULL
;
1313 files_struct
*fsp
= NULL
;
1317 int saved_errno
= 0;
1320 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1325 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1330 ai
= afpinfo_new(talloc_tos());
1335 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1337 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1338 if (aiblob
.data
== NULL
) {
1343 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1345 if (size
!= AFP_INFO_SIZE
) {
1349 stream_name
= synthetic_smb_fname(talloc_tos(),
1350 smb_fname
->base_name
,
1355 if (stream_name
== NULL
) {
1356 data_blob_free(&aiblob
);
1357 DBG_ERR("synthetic_smb_fname failed\n");
1361 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1363 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, stream_name
);
1364 if (!NT_STATUS_IS_OK(status
) &&
1365 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
))
1370 status
= SMB_VFS_CREATE_FILE(
1371 handle
->conn
, /* conn */
1374 stream_name
, /* fname */
1375 FILE_GENERIC_WRITE
, /* access_mask */
1376 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1377 FILE_OPEN_IF
, /* create_disposition */
1378 0, /* create_options */
1379 0, /* file_attributes */
1380 INTERNAL_OPEN_ONLY
, /* oplock_request */
1382 0, /* allocation_size */
1383 0, /* private_flags */
1388 NULL
, NULL
); /* create context */
1389 TALLOC_FREE(stream_name
);
1390 if (!NT_STATUS_IS_OK(status
)) {
1391 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1395 nwritten
= SMB_VFS_PWRITE(fsp
,
1399 if (nwritten
== -1) {
1400 DBG_ERR("SMB_VFS_PWRITE failed\n");
1401 saved_errno
= errno
;
1402 close_file_free(NULL
, &fsp
, ERROR_CLOSE
);
1403 errno
= saved_errno
;
1407 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1408 if (!NT_STATUS_IS_OK(status
)) {
1416 static bool ad_convert_truncate(vfs_handle_struct
*handle
,
1418 const struct smb_filename
*smb_fname
)
1423 newlen
= ADEDOFF_RFORK_DOT_UND
+ ad_getentrylen(ad
, ADEID_RFORK
);
1425 rc
= SMB_VFS_FTRUNCATE(ad
->ad_fsp
, newlen
);
1433 static bool ad_convert_blank_rfork(vfs_handle_struct
*handle
,
1438 size_t rforklen
= sizeof(empty_resourcefork
);
1446 if (!(flags
& AD_CONV_WIPE_BLANK
)) {
1450 if (ad_getentrylen(ad
, ADEID_RFORK
) != rforklen
) {
1454 nread
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, ADEDOFF_RFORK_DOT_UND
);
1455 if (nread
!= rforklen
) {
1456 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1457 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1461 cmp
= memcmp(buf
, empty_resourcefork
, rforklen
);
1466 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1468 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1470 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1478 static bool ad_convert_delete_adfile(vfs_handle_struct
*handle
,
1480 const struct smb_filename
*smb_fname
,
1483 struct smb_filename
*parent_fname
= NULL
;
1484 struct smb_filename
*at_fname
= NULL
;
1485 struct smb_filename
*ad_name
= NULL
;
1489 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1493 if (!(flags
& AD_CONV_DELETE
)) {
1497 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1502 status
= parent_pathref(talloc_tos(),
1503 handle
->conn
->cwd_fsp
,
1507 TALLOC_FREE(ad_name
);
1508 if (!NT_STATUS_IS_OK(status
)) {
1512 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
1517 DBG_ERR("Unlinking [%s/%s] failed: %s\n",
1518 smb_fname_str_dbg(parent_fname
),
1519 smb_fname_str_dbg(at_fname
), strerror(errno
));
1520 TALLOC_FREE(parent_fname
);
1524 DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1525 smb_fname_str_dbg(parent_fname
),
1526 smb_fname_str_dbg(at_fname
));
1527 TALLOC_FREE(parent_fname
);
1533 * Convert from Apple's ._ file to Netatalk
1535 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1536 * bytes containing packed xattrs.
1538 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1541 int ad_convert(struct vfs_handle_struct
*handle
,
1542 const struct smb_filename
*smb_fname
,
1543 const char *catia_mappings
,
1546 struct adouble
*ad
= NULL
;
1548 bool converted_xattr
= false;
1552 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
1557 ok
= ad_convert_xattr(handle
,
1567 ok
= ad_convert_blank_rfork(handle
, ad
, flags
, &blank
);
1573 if (converted_xattr
|| blank
) {
1574 ok
= ad_convert_truncate(handle
, ad
, smb_fname
);
1581 ok
= ad_convert_finderinfo(handle
, ad
, smb_fname
);
1583 DBG_ERR("Failed to convert [%s]\n",
1584 smb_fname_str_dbg(smb_fname
));
1589 ok
= ad_convert_delete_adfile(handle
,
1604 static bool ad_unconvert_open_ad(TALLOC_CTX
*mem_ctx
,
1605 struct vfs_handle_struct
*handle
,
1606 struct smb_filename
*smb_fname
,
1607 struct smb_filename
*adpath
,
1608 files_struct
**_fsp
)
1610 files_struct
*fsp
= NULL
;
1614 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, adpath
);
1615 if (!NT_STATUS_IS_OK(status
) &&
1616 !NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
1620 status
= SMB_VFS_CREATE_FILE(
1625 FILE_READ_DATA
|FILE_WRITE_DATA
,
1626 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1628 0, /* create_options */
1629 0, /* file_attributes */
1632 0, /* allocation_size */
1633 0, /* private_flags */
1638 NULL
, NULL
); /* create context */
1639 if (!NT_STATUS_IS_OK(status
)) {
1640 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1641 smb_fname_str_dbg(adpath
), nt_errstr(status
));
1645 if (fsp
->fsp_name
->st
.st_ex_uid
!= smb_fname
->st
.st_ex_uid
||
1646 fsp
->fsp_name
->st
.st_ex_gid
!= smb_fname
->st
.st_ex_gid
)
1648 ret
= SMB_VFS_FCHOWN(fsp
,
1649 smb_fname
->st
.st_ex_uid
,
1650 smb_fname
->st
.st_ex_gid
);
1652 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1653 fsp_str_dbg(fsp
), nt_errstr(status
));
1654 close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1663 static bool ad_unconvert_get_streams(struct vfs_handle_struct
*handle
,
1664 struct smb_filename
*smb_fname
,
1665 TALLOC_CTX
*mem_ctx
,
1666 unsigned int *num_streams
,
1667 struct stream_struct
**streams
)
1669 files_struct
*fsp
= NULL
;
1672 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, smb_fname
);
1673 if (!NT_STATUS_IS_OK(status
)) {
1677 status
= SMB_VFS_CREATE_FILE(
1678 handle
->conn
, /* conn */
1681 smb_fname
, /* fname */
1682 FILE_READ_ATTRIBUTES
, /* access_mask */
1683 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
1685 FILE_OPEN
, /* create_disposition*/
1686 0, /* create_options */
1687 0, /* file_attributes */
1688 INTERNAL_OPEN_ONLY
, /* oplock_request */
1690 0, /* allocation_size */
1691 0, /* private_flags */
1696 NULL
, NULL
); /* create context */
1697 if (!NT_STATUS_IS_OK(status
)) {
1698 DBG_ERR("Opening [%s] failed: %s\n",
1699 smb_fname_str_dbg(smb_fname
),
1704 status
= vfs_fstreaminfo(fsp
,
1708 if (!NT_STATUS_IS_OK(status
)) {
1709 close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1710 DBG_ERR("streaminfo on [%s] failed: %s\n",
1711 smb_fname_str_dbg(smb_fname
),
1716 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1717 if (!NT_STATUS_IS_OK(status
)) {
1718 DBG_ERR("close_file [%s] failed: %s\n",
1719 smb_fname_str_dbg(smb_fname
),
1727 struct ad_collect_state
{
1729 size_t adx_data_off
;
1730 char *rsrc_data_buf
;
1733 static bool ad_collect_one_stream(struct vfs_handle_struct
*handle
,
1734 struct char_mappings
**cmaps
,
1735 struct smb_filename
*smb_fname
,
1736 const struct stream_struct
*stream
,
1738 struct ad_collect_state
*state
)
1740 struct smb_filename
*sname
= NULL
;
1741 files_struct
*fsp
= NULL
;
1742 struct ad_xattr_entry
*e
= NULL
;
1743 char *mapped_name
= NULL
;
1750 sname
= synthetic_smb_fname(ad
,
1751 smb_fname
->base_name
,
1756 if (sname
== NULL
) {
1760 if (is_ntfs_default_stream_smb_fname(sname
)) {
1765 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname
));
1767 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, sname
);
1768 if (!NT_STATUS_IS_OK(status
)) {
1773 status
= SMB_VFS_CREATE_FILE(
1778 FILE_READ_DATA
|DELETE_ACCESS
,
1781 0, /* create_options */
1782 0, /* file_attributes */
1783 INTERNAL_OPEN_ONLY
, /* oplock_request */
1785 0, /* allocation_size */
1786 0, /* private_flags */
1791 NULL
, NULL
); /* create context */
1792 if (!NT_STATUS_IS_OK(status
)) {
1793 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1794 smb_fname_str_dbg(sname
));
1799 if (is_afpinfo_stream(stream
->name
)) {
1800 char buf
[AFP_INFO_SIZE
];
1802 if (stream
->size
!= AFP_INFO_SIZE
) {
1803 DBG_ERR("Bad size [%zd] on [%s]\n",
1804 (ssize_t
)stream
->size
,
1805 smb_fname_str_dbg(sname
));
1810 nread
= SMB_VFS_PREAD(fsp
, buf
, stream
->size
, 0);
1811 if (nread
!= AFP_INFO_SIZE
) {
1812 DBG_ERR("Bad size [%zd] on [%s]\n",
1813 (ssize_t
)stream
->size
,
1814 smb_fname_str_dbg(sname
));
1819 memcpy(ad
->ad_data
+ ADEDOFF_FINDERI_DOT_UND
,
1820 buf
+ AFP_OFF_FinderInfo
,
1823 ok
= set_delete_on_close(fsp
,
1825 fsp
->conn
->session_info
->security_token
,
1826 fsp
->conn
->session_info
->unix_token
);
1828 DBG_ERR("Deleting [%s] failed\n",
1829 smb_fname_str_dbg(sname
));
1837 if (is_afpresource_stream(stream
->name
)) {
1838 ad
->ad_rsrc_data
= talloc_size(ad
, stream
->size
);
1839 if (ad
->ad_rsrc_data
== NULL
) {
1844 nread
= SMB_VFS_PREAD(fsp
,
1848 if (nread
!= stream
->size
) {
1849 DBG_ERR("Bad size [%zd] on [%s]\n",
1850 (ssize_t
)stream
->size
,
1851 smb_fname_str_dbg(sname
));
1856 ad_setentrylen(ad
, ADEID_RFORK
, stream
->size
);
1858 if (!state
->have_adfile
) {
1860 * We have a resource *stream* but no AppleDouble
1861 * sidecar file, this means the share is configured with
1862 * fruit:resource=stream. So we should delete the
1865 ok
= set_delete_on_close(
1868 fsp
->conn
->session_info
->security_token
,
1869 fsp
->conn
->session_info
->unix_token
);
1871 DBG_ERR("Deleting [%s] failed\n",
1872 smb_fname_str_dbg(sname
));
1881 ad
->adx_entries
= talloc_realloc(ad
,
1883 struct ad_xattr_entry
,
1884 ad
->adx_header
.adx_num_attrs
+ 1);
1885 if (ad
->adx_entries
== NULL
) {
1890 e
= &ad
->adx_entries
[ad
->adx_header
.adx_num_attrs
];
1891 *e
= (struct ad_xattr_entry
) {
1892 .adx_length
= stream
->size
,
1894 e
->adx_name
= talloc_strdup(ad
, stream
->name
+ 1);
1895 if (e
->adx_name
== NULL
) {
1899 p
= strchr(e
->adx_name
, ':');
1904 status
= string_replace_allocate(handle
->conn
,
1909 vfs_translate_to_unix
);
1910 if (!NT_STATUS_IS_OK(status
) &&
1911 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1913 DBG_ERR("string_replace_allocate failed\n");
1918 e
->adx_name
= mapped_name
;
1919 e
->adx_namelen
= strlen(e
->adx_name
) + 1,
1921 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1922 ad
->adx_header
.adx_num_attrs
,
1924 (size_t)e
->adx_length
);
1926 ad
->adx_header
.adx_num_attrs
++;
1928 needed_size
= state
->adx_data_off
+ stream
->size
;
1929 if (needed_size
> talloc_get_size(ad
->adx_data
)) {
1930 ad
->adx_data
= talloc_realloc(ad
,
1934 if (ad
->adx_data
== NULL
) {
1940 nread
= SMB_VFS_PREAD(fsp
,
1941 ad
->adx_data
+ state
->adx_data_off
,
1944 if (nread
!= stream
->size
) {
1945 DBG_ERR("Bad size [%zd] on [%s]\n",
1946 (ssize_t
)stream
->size
,
1947 smb_fname_str_dbg(sname
));
1951 state
->adx_data_off
+= nread
;
1953 ok
= set_delete_on_close(fsp
,
1955 fsp
->conn
->session_info
->security_token
,
1956 fsp
->conn
->session_info
->unix_token
);
1958 DBG_ERR("Deleting [%s] failed\n",
1959 smb_fname_str_dbg(sname
));
1967 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1968 if (!NT_STATUS_IS_OK(status
)) {
1969 DBG_ERR("close_file [%s] failed: %s\n",
1970 smb_fname_str_dbg(smb_fname
),
1980 * Convert filesystem metadata to AppleDouble file
1982 bool ad_unconvert(TALLOC_CTX
*mem_ctx
,
1983 struct vfs_handle_struct
*handle
,
1984 const char *catia_mappings
,
1985 struct smb_filename
*smb_fname
,
1988 static struct char_mappings
**cmaps
= NULL
;
1989 TALLOC_CTX
*frame
= talloc_stackframe();
1990 struct ad_collect_state state
;
1991 struct stream_struct
*streams
= NULL
;
1992 struct smb_filename
*adpath
= NULL
;
1993 struct adouble
*ad
= NULL
;
1994 unsigned int num_streams
= 0;
1995 size_t to_convert
= 0;
1996 bool have_rsrc
= false;
1997 files_struct
*fsp
= NULL
;
2005 if (cmaps
== NULL
) {
2006 const char **mappings
= NULL
;
2008 mappings
= str_list_make_v3_const(
2009 frame
, catia_mappings
, NULL
);
2010 if (mappings
== NULL
) {
2014 cmaps
= string_replace_init_map(mem_ctx
, mappings
);
2015 TALLOC_FREE(mappings
);
2018 ok
= ad_unconvert_get_streams(handle
,
2027 for (i
= 0; i
< num_streams
; i
++) {
2028 if (strcasecmp_m(streams
[i
].name
, "::$DATA") == 0) {
2032 if (is_afpresource_stream(streams
[i
].name
)) {
2037 if (to_convert
== 0) {
2042 state
= (struct ad_collect_state
) {
2046 ret
= adouble_path(frame
, smb_fname
, &adpath
);
2052 ret
= SMB_VFS_STAT(handle
->conn
, adpath
);
2054 state
.have_adfile
= true;
2056 if (errno
!= ENOENT
) {
2060 state
.have_adfile
= false;
2063 if (to_convert
== 1 && have_rsrc
&& state
.have_adfile
) {
2065 * So we have just a single stream, the resource fork stream
2066 * from an AppleDouble file. Fine, that means there's nothing to
2073 ad
= ad_init(frame
, ADOUBLE_RSRC
);
2079 for (i
= 0; i
< num_streams
; i
++) {
2080 ok
= ad_collect_one_stream(handle
,
2091 ok
= ad_unconvert_open_ad(frame
, handle
, smb_fname
, adpath
, &fsp
);
2093 DBG_ERR("Failed to open adfile [%s]\n",
2094 smb_fname_str_dbg(smb_fname
));
2098 ret
= ad_fset(handle
, ad
, fsp
);
2109 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
2110 if (!NT_STATUS_IS_OK(status
)) {
2111 DBG_ERR("close_file_free() [%s] failed: %s\n",
2112 smb_fname_str_dbg(smb_fname
),
2122 * Read and parse Netatalk AppleDouble metadata xattr
2124 static ssize_t
ad_read_meta(vfs_handle_struct
*handle
,
2126 const struct smb_filename
*smb_fname
)
2131 struct files_struct
*fsp
= smb_fname
->fsp
;
2133 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
2135 fsp
= metadata_fsp(fsp
);
2137 ealen
= SMB_VFS_FGETXATTR(fsp
,
2138 AFPINFO_EA_NETATALK
,
2146 if (errno
== ENOATTR
) {
2152 DEBUG(2, ("error reading meta xattr: %s\n",
2158 if (ealen
!= AD_DATASZ_XATTR
) {
2159 DEBUG(2, ("bad size %zd\n", ealen
));
2165 /* Now parse entries */
2166 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
2169 "Invalid AppleDouble xattr metadata (%s) in file: %s - "
2170 "Consider deleting the corrupted file.\n",
2171 smb_fname
->base_name
,
2172 ad
->ad_fsp
->fsp_name
->base_name
);
2178 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
2179 || !ad_getentryoff(ad
, ADEID_COMMENT
)
2180 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
2181 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
2182 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
2183 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
2184 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
2185 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
2186 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2193 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2194 smb_fname
->base_name
, rc
));
2198 if (errno
== EINVAL
) {
2200 (void)SMB_VFS_FREMOVEXATTR(fsp
,
2201 AFPINFO_EA_NETATALK
);
2209 static NTSTATUS
adouble_open_rsrc_fsp(const struct files_struct
*dirfsp
,
2210 const struct smb_filename
*smb_base_fname
,
2213 struct files_struct
**_ad_fsp
)
2216 struct adouble
*ad
= NULL
;
2217 struct smb_filename
*adp_smb_fname
= NULL
;
2218 struct files_struct
*ad_fsp
= NULL
;
2220 struct vfs_open_how how
= { .flags
= in_flags
, .mode
= mode
, };
2222 rc
= adouble_path(talloc_tos(),
2226 return NT_STATUS_NO_MEMORY
;
2229 status
= create_internal_fsp(dirfsp
->conn
,
2232 if (!NT_STATUS_IS_OK(status
)) {
2237 how
.flags
&= ~(O_PATH
);
2239 if (how
.flags
& (O_CREAT
| O_TRUNC
| O_WRONLY
)) {
2240 /* We always need read/write access for the metadata header too */
2241 how
.flags
&= ~(O_WRONLY
);
2242 how
.flags
|= O_RDWR
;
2245 status
= fd_openat(dirfsp
,
2249 if (!NT_STATUS_IS_OK(status
)) {
2250 file_free(NULL
, ad_fsp
);
2254 if (how
.flags
& (O_CREAT
| O_TRUNC
)) {
2255 ad
= ad_init(talloc_tos(), ADOUBLE_RSRC
);
2257 file_free(NULL
, ad_fsp
);
2258 return NT_STATUS_NO_MEMORY
;
2261 rc
= ad_fset(ad_fsp
->conn
->vfs_handles
, ad
, ad_fsp
);
2263 file_free(NULL
, ad_fsp
);
2264 return NT_STATUS_IO_DEVICE_ERROR
;
2270 return NT_STATUS_OK
;
2273 NTSTATUS
adouble_open_from_base_fsp(const struct files_struct
*dirfsp
,
2274 struct files_struct
*base_fsp
,
2275 adouble_type_t type
,
2278 struct files_struct
**_ad_fsp
)
2282 SMB_ASSERT(base_fsp
!= NULL
);
2283 SMB_ASSERT(!fsp_is_alternate_stream(base_fsp
));
2287 return NT_STATUS_INTERNAL_ERROR
;
2289 return adouble_open_rsrc_fsp(dirfsp
,
2296 return NT_STATUS_INTERNAL_ERROR
;
2300 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2301 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2302 * for file IO on the ._ file.
2304 static int ad_open(vfs_handle_struct
*handle
,
2307 const struct smb_filename
*smb_fname
,
2313 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
2314 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2316 if (ad
->ad_type
== ADOUBLE_META
) {
2322 ad
->ad_opened
= false;
2326 status
= adouble_open_rsrc_fsp(handle
->conn
->cwd_fsp
,
2331 if (!NT_STATUS_IS_OK(status
)) {
2332 errno
= map_errno_from_nt_status(status
);
2335 ad
->ad_opened
= true;
2337 DBG_DEBUG("Path [%s] type [%s]\n",
2338 smb_fname
->base_name
,
2339 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2344 static ssize_t
ad_read_rsrc_adouble(vfs_handle_struct
*handle
,
2346 const struct smb_filename
*smb_fname
)
2353 ret
= SMB_VFS_NEXT_FSTAT(handle
, ad
->ad_fsp
, &ad
->ad_fsp
->fsp_name
->st
);
2355 DBG_ERR("fstat [%s] failed: %s\n",
2356 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
2360 to_read
= ad
->ad_fsp
->fsp_name
->st
.st_ex_size
;
2361 if (to_read
> AD_XATTR_MAX_HDR_SIZE
) {
2362 to_read
= AD_XATTR_MAX_HDR_SIZE
;
2365 len
= SMB_VFS_NEXT_PREAD(handle
,
2370 if (len
!= to_read
) {
2371 DBG_NOTICE("%s %s: bad size: %zd\n",
2372 smb_fname
->base_name
, strerror(errno
), len
);
2376 /* Now parse entries */
2379 ad
->ad_fsp
->fsp_name
->st
.st_ex_size
);
2381 DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2382 "Consider deleting the corrupted file.\n",
2383 smb_fname
->base_name
,
2384 ad
->ad_fsp
->fsp_name
->base_name
);
2389 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
2390 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
2391 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
))
2393 DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2394 "Consider deleting the corrupted file.\n",
2395 smb_fname
->base_name
,
2396 ad
->ad_fsp
->fsp_name
->base_name
);
2405 * Read and parse resource fork, either ._ AppleDouble file or xattr
2407 static ssize_t
ad_read_rsrc(vfs_handle_struct
*handle
,
2409 const struct smb_filename
*smb_fname
)
2411 return ad_read_rsrc_adouble(handle
, ad
, smb_fname
);
2415 * Read and unpack an AppleDouble metadata xattr or resource
2417 static ssize_t
ad_read(vfs_handle_struct
*handle
,
2419 const struct smb_filename
*smb_fname
)
2421 switch (ad
->ad_type
) {
2423 return ad_read_meta(handle
, ad
, smb_fname
);
2425 return ad_read_rsrc(handle
, ad
, smb_fname
);
2431 static int adouble_destructor(struct adouble
*ad
)
2435 if (!ad
->ad_opened
) {
2439 SMB_ASSERT(ad
->ad_fsp
!= NULL
);
2441 status
= fd_close(ad
->ad_fsp
);
2442 if (!NT_STATUS_IS_OK(status
)) {
2443 DBG_ERR("Closing [%s] failed: %s\n",
2444 fsp_str_dbg(ad
->ad_fsp
), nt_errstr(status
));
2446 file_free(NULL
, ad
->ad_fsp
);
2448 ad
->ad_opened
= false;
2454 * Allocate a struct adouble without initialiing it
2456 * The struct is either hang of the fsp extension context or if fsp is
2459 * @param[in] ctx talloc context
2460 * @param[in] handle vfs handle
2461 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2463 * @return adouble handle
2465 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
,
2466 adouble_type_t type
)
2474 adsize
= AD_DATASZ_XATTR
;
2478 * AppleDouble ._ file case, optimize for fewer (but larger)
2481 * - without xattrs size of the header is exactly
2482 * AD_DATASZ_DOT_UND (82) bytes
2484 * - with embedded xattrs it can be larger, up to
2485 * AD_XATTR_MAX_HDR_SIZE
2487 * Larger headers are not supported, but this is a reasonable
2488 * limit that is also employed by the macOS client.
2490 * We used the largest possible size to be able to read the full
2491 * header with one IO.
2493 adsize
= AD_XATTR_MAX_HDR_SIZE
;
2499 ad
= talloc_zero(ctx
, struct adouble
);
2506 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
2507 if (ad
->ad_data
== NULL
) {
2514 ad
->ad_magic
= AD_MAGIC
;
2515 ad
->ad_version
= AD_VERSION
;
2517 talloc_set_destructor(ad
, adouble_destructor
);
2527 * Allocate and initialize a new struct adouble
2529 * @param[in] ctx talloc context
2530 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2532 * @return adouble handle, initialized
2534 struct adouble
*ad_init(TALLOC_CTX
*ctx
, adouble_type_t type
)
2537 const struct ad_entry_order
*eid
;
2538 struct adouble
*ad
= NULL
;
2539 time_t t
= time(NULL
);
2543 eid
= entry_order_meta_xattr
;
2546 eid
= entry_order_dot_und
;
2552 ad
= ad_alloc(ctx
, type
);
2558 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
2559 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
2563 /* put something sane in the date fields */
2564 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
2565 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
2566 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
2567 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
2575 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
2576 vfs_handle_struct
*handle
,
2578 const struct smb_filename
*smb_fname
,
2579 adouble_type_t type
)
2583 struct adouble
*ad
= NULL
;
2587 if (fsp_is_alternate_stream(fsp
)) {
2588 smb_fname
= fsp
->base_fsp
->fsp_name
;
2590 smb_fname
= fsp
->fsp_name
;
2594 DEBUG(10, ("ad_get(%s) called for %s\n",
2595 type
== ADOUBLE_META
? "meta" : "rsrc",
2596 smb_fname
!= NULL
? smb_fname
->base_name
: "???"));
2598 ad
= ad_alloc(ctx
, type
);
2604 /* Try rw first so we can use the fd in ad_convert() */
2607 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2608 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
2610 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2613 DBG_DEBUG("ad_open [%s] error [%s]\n",
2614 smb_fname
->base_name
, strerror(errno
));
2619 len
= ad_read(handle
, ad
, smb_fname
);
2621 DEBUG(10, ("error reading AppleDouble for %s\n",
2622 smb_fname
->base_name
));
2628 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2629 type
== ADOUBLE_META
? "meta" : "rsrc",
2630 smb_fname
->base_name
, rc
));
2639 * Return AppleDouble data for a file
2641 * @param[in] ctx talloc context
2642 * @param[in] handle vfs handle
2643 * @param[in] smb_fname pathname to file or directory
2644 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2646 * @return talloced struct adouble or NULL on error
2648 struct adouble
*ad_get(TALLOC_CTX
*ctx
,
2649 vfs_handle_struct
*handle
,
2650 const struct smb_filename
*smb_fname
,
2651 adouble_type_t type
)
2653 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
2657 * Return AppleDouble data for a file
2659 * @param[in] ctx talloc context
2660 * @param[in] handle vfs handle
2661 * @param[in] fsp fsp to use for IO
2662 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2664 * @return talloced struct adouble or NULL on error
2666 struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
2667 files_struct
*fsp
, adouble_type_t type
)
2669 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
2673 * Set AppleDouble metadata on a file or directory
2675 * @param[in] ad adouble handle
2676 * @param[in] fsp file handle
2678 * @return status code, 0 means success
2680 int ad_fset(struct vfs_handle_struct
*handle
,
2688 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2690 ok
= ad_pack(handle
, ad
, fsp
);
2695 switch (ad
->ad_type
) {
2697 rc
= SMB_VFS_NEXT_FSETXATTR(handle
,
2698 fsp
->base_fsp
? fsp
->base_fsp
: fsp
,
2699 AFPINFO_EA_NETATALK
,
2701 AD_DATASZ_XATTR
, 0);
2704 len
= SMB_VFS_NEXT_PWRITE(handle
,
2707 ad_getentryoff(ad
, ADEID_RFORK
),
2709 if (len
!= ad_getentryoff(ad
, ADEID_RFORK
)) {
2710 DBG_ERR("short write on %s: %zd\n", fsp_str_dbg(fsp
), len
);
2720 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2725 bool is_adouble_file(const char *path
)
2727 const char *p
= NULL
;
2730 p
= strrchr(path
, '/');
2738 ADOUBLE_NAME_PREFIX
,
2739 strlen(ADOUBLE_NAME_PREFIX
));
2747 * Prepend "._" to a basename
2748 * Return a new struct smb_filename with stream_name == NULL.
2750 int adouble_path(TALLOC_CTX
*ctx
,
2751 const struct smb_filename
*smb_fname_in
,
2752 struct smb_filename
**pp_smb_fname_out
)
2756 struct smb_filename
*smb_fname
= NULL
;
2758 smb_fname
= cp_smb_filename_nostream(ctx
, smb_fname_in
);
2759 if (smb_fname
== NULL
) {
2763 /* We're replacing base_name. */
2764 TALLOC_FREE(smb_fname
->base_name
);
2766 SET_STAT_INVALID(smb_fname
->st
);
2768 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2770 TALLOC_FREE(smb_fname
);
2774 if (ISDOT(parent
)) {
2775 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2778 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2779 "%s/._%s", parent
, base
);
2781 if (smb_fname
->base_name
== NULL
) {
2782 TALLOC_FREE(smb_fname
);
2786 *pp_smb_fname_out
= smb_fname
;
2792 * Allocate and initialize an AfpInfo struct
2794 AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2796 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2800 ai
->afpi_Signature
= AFP_Signature
;
2801 ai
->afpi_Version
= AFP_Version
;
2802 ai
->afpi_BackupTime
= AD_DATE_START
;
2807 * Pack an AfpInfo struct into a buffer
2809 * Buffer size must be at least AFP_INFO_SIZE
2810 * Returns size of packed buffer
2812 ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2814 memset(buf
, 0, AFP_INFO_SIZE
);
2816 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2817 RSIVAL(buf
, 4, ai
->afpi_Version
);
2818 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2819 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2821 return AFP_INFO_SIZE
;
2825 * Unpack a buffer into a AfpInfo structure
2827 * Buffer size must be at least AFP_INFO_SIZE
2828 * Returns allocated AfpInfo struct
2830 AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
, bool validate
)
2832 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2837 ai
->afpi_Signature
= RIVAL(data
, 0);
2838 ai
->afpi_Version
= RIVAL(data
, 4);
2839 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2840 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2841 sizeof(ai
->afpi_FinderInfo
));
2844 if (ai
->afpi_Signature
!= AFP_Signature
2845 || ai
->afpi_Version
!= AFP_Version
)
2847 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2851 ai
->afpi_Signature
= AFP_Signature
;
2852 ai
->afpi_Version
= AFP_Version
;