2 * Samba AppleDouble helpers
4 * Copyright (C) Ralph Boehme, 2019
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "MacExtensions.h"
23 #include "string_replace.h"
24 #include "smbd/smbd.h"
25 #include "system/filesys.h"
26 #include "libcli/security/security.h"
27 #include "lib/util_macstreams.h"
31 "._" AppleDouble Header File Layout:
37 .-- AD ENTRY[0] Finder Info Entry (must be first)
38 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
40 | '-> FINDER INFO Fixed Size Data (32 Bytes)
41 | ~~~~~~~~~~~~~ 2 Bytes Padding
42 | EXT ATTR HDR Fixed Size Data (36 Bytes)
45 | ATTR ENTRY[1] --+--.
46 | ATTR ENTRY[2] --+--+--.
48 | ATTR ENTRY[N] --+--+--+--.
49 | ATTR DATA 0 <-' | | |
51 | ATTR DATA 1 <----' | |
53 | ATTR DATA 2 <-------' |
56 | ATTR DATA N <----------'
58 | ... Attribute Free Space
61 ... Variable Sized Data
65 /* Number of actually used entries */
66 #define ADEID_NUM_XATTR 8
67 #define ADEID_NUM_DOT_UND 2
68 #define ADEID_NUM_RSRC_XATTR 1
70 /* Sizes of relevant entry bits */
71 #define ADEDLEN_MAGIC 4
72 #define ADEDLEN_VERSION 4
73 #define ADEDLEN_FILLER 16
74 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
75 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
76 #define ADEDLEN_NENTRIES 2
77 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
78 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
79 #define AD_ENTRY_LEN_EID 4
80 #define AD_ENTRY_LEN_OFF 4
81 #define AD_ENTRY_LEN_LEN 4
82 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
85 #define ADEDOFF_MAGIC 0
86 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
87 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
88 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
90 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
91 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
92 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
93 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
94 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
96 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
97 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
98 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
99 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
101 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
102 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
103 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
105 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
106 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
107 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
108 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
109 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
110 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
112 #if AD_DATASZ_XATTR != 402
113 #error bad size for AD_DATASZ_XATTR
116 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
117 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
119 #if AD_DATASZ_DOT_UND != 82
120 #error bad size for AD_DATASZ_DOT_UND
123 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
124 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
125 #define AD_XATTR_HDR_SIZE 36
126 #define AD_XATTR_MAX_HDR_SIZE 65536
127 #define ADX_ENTRY_FIXED_SIZE (4+4+2+1)
130 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131 * representation as well as the on-disk format.
133 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
134 * the length of the FinderInfo entry is larger then 32 bytes. It is then
135 * preceeded with 2 bytes padding.
137 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
140 struct ad_xattr_header
{
141 uint32_t adx_magic
; /* ATTR_HDR_MAGIC */
142 uint32_t adx_debug_tag
; /* for debugging == file id of owning file */
143 uint32_t adx_total_size
; /* file offset of end of attribute header + entries + data */
144 uint32_t adx_data_start
; /* file offset to attribute data area */
145 uint32_t adx_data_length
; /* length of attribute data area */
146 uint32_t adx_reserved
[3];
148 uint16_t adx_num_attrs
;
151 /* On-disk entries are aligned on 4 byte boundaries */
152 struct ad_xattr_entry
{
153 uint32_t adx_offset
; /* file offset to data */
154 uint32_t adx_length
; /* size of attribute data */
156 uint8_t adx_namelen
; /* included the NULL terminator */
157 char *adx_name
; /* NULL-terminated UTF-8 name */
166 files_struct
*ad_fsp
;
168 adouble_type_t ad_type
;
171 uint8_t ad_filler
[ADEDLEN_FILLER
];
172 struct ad_entry ad_eid
[ADEID_MAX
];
175 struct ad_xattr_header adx_header
;
176 struct ad_xattr_entry
*adx_entries
;
180 struct ad_entry_order
{
181 uint32_t id
, offset
, len
;
184 /* Netatalk AppleDouble metadata xattr */
186 struct ad_entry_order entry_order_meta_xattr
[ADEID_NUM_XATTR
+ 1] = {
187 {ADEID_FINDERI
, ADEDOFF_FINDERI_XATTR
, ADEDLEN_FINDERI
},
188 {ADEID_COMMENT
, ADEDOFF_COMMENT_XATTR
, 0},
189 {ADEID_FILEDATESI
, ADEDOFF_FILEDATESI_XATTR
, ADEDLEN_FILEDATESI
},
190 {ADEID_AFPFILEI
, ADEDOFF_AFPFILEI_XATTR
, ADEDLEN_AFPFILEI
},
191 {ADEID_PRIVDEV
, ADEDOFF_PRIVDEV_XATTR
, 0},
192 {ADEID_PRIVINO
, ADEDOFF_PRIVINO_XATTR
, 0},
193 {ADEID_PRIVSYN
, ADEDOFF_PRIVSYN_XATTR
, 0},
194 {ADEID_PRIVID
, ADEDOFF_PRIVID_XATTR
, 0},
198 /* AppleDouble resource fork file (the ones prefixed by "._") */
200 struct ad_entry_order entry_order_dot_und
[ADEID_NUM_DOT_UND
+ 1] = {
201 {ADEID_FINDERI
, ADEDOFF_FINDERI_DOT_UND
, ADEDLEN_FINDERI
},
202 {ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
, 0},
206 /* Conversion from enumerated id to on-disk AppleDouble id */
207 #define AD_EID_DISK(a) (set_eid[a])
208 static const uint32_t set_eid
[] = {
209 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
210 AD_DEV
, AD_INO
, AD_SYN
, AD_ID
213 static char empty_resourcefork
[] = {
214 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
216 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
217 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
218 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
219 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
220 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
221 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
252 size_t ad_getentrylen(const struct adouble
*ad
, int eid
)
254 return ad
->ad_eid
[eid
].ade_len
;
257 size_t ad_getentryoff(const struct adouble
*ad
, int eid
)
259 return ad
->ad_eid
[eid
].ade_off
;
262 size_t ad_setentrylen(struct adouble
*ad
, int eid
, size_t len
)
264 return ad
->ad_eid
[eid
].ade_len
= len
;
267 size_t ad_setentryoff(struct adouble
*ad
, int eid
, size_t off
)
269 return ad
->ad_eid
[eid
].ade_off
= off
;
273 * Return a pointer to an AppleDouble entry
275 * Returns NULL if the entry is not present
277 char *ad_get_entry(const struct adouble
*ad
, int eid
)
279 off_t off
= ad_getentryoff(ad
, eid
);
280 size_t len
= ad_getentrylen(ad
, eid
);
282 if (off
== 0 || len
== 0) {
286 return ad
->ad_data
+ off
;
292 int ad_getdate(const struct adouble
*ad
, unsigned int dateoff
, uint32_t *date
)
294 bool xlate
= (dateoff
& AD_DATE_UNIX
);
297 dateoff
&= AD_DATE_MASK
;
298 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
303 if (dateoff
> AD_DATE_ACCESS
) {
307 memcpy(date
, p
+ dateoff
, sizeof(uint32_t));
310 *date
= AD_DATE_TO_UNIX(*date
);
318 int ad_setdate(struct adouble
*ad
, unsigned int dateoff
, uint32_t date
)
320 bool xlate
= (dateoff
& AD_DATE_UNIX
);
323 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
328 dateoff
&= AD_DATE_MASK
;
330 date
= AD_DATE_FROM_UNIX(date
);
333 if (dateoff
> AD_DATE_ACCESS
) {
337 memcpy(p
+ dateoff
, &date
, sizeof(date
));
344 * Map on-disk AppleDouble id to enumerated id
346 static uint32_t get_eid(uint32_t eid
)
354 return ADEID_PRIVDEV
;
356 return ADEID_PRIVINO
;
358 return ADEID_PRIVSYN
;
369 * Move resourcefork data in an AppleDouble file
371 * This is supposed to make room in an AppleDouble file by moving the
372 * resourcefork data behind the space required for packing additional xattr data
373 * in the extended FinderInfo entry.
375 * When we're called we're expecting an AppleDouble file with just two entries
376 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
377 * offset of ADEDOFF_RFORK_DOT_UND.
379 static bool ad_pack_move_reso(struct vfs_handle_struct
*handle
,
388 reso_len
= ad_getentrylen(ad
, ADEID_RFORK
);
389 reso_off
= ad_getentryoff(ad
, ADEID_RFORK
);
395 if (ad
->ad_rsrc_data
== NULL
) {
397 * This buffer is already set when converting a resourcefork
398 * stream from vfs_streams_depot backend via ad_unconvert(). It
399 * is NULL with vfs_streams_xattr where the resourcefork stream
400 * is stored in an AppleDouble sidecar file vy vfs_fruit.
402 ad
->ad_rsrc_data
= talloc_size(ad
, reso_len
);
403 if (ad
->ad_rsrc_data
== NULL
) {
407 n
= SMB_VFS_NEXT_PREAD(handle
,
411 ADEDOFF_RFORK_DOT_UND
);
413 DBG_ERR("Read on [%s] failed\n",
420 n
= SMB_VFS_NEXT_PWRITE(handle
,
426 DBG_ERR("Write on [%s] failed\n",
437 static bool ad_pack_xattrs(struct vfs_handle_struct
*handle
,
441 struct ad_xattr_header
*h
= &ad
->adx_header
;
448 if (ad
->adx_entries
== NULL
) {
449 /* No xattrs, nothing to pack */
454 DBG_ERR("fsp unexpectedly NULL\n");
458 oldsize
= talloc_get_size(ad
->ad_data
);
459 if (oldsize
< AD_XATTR_MAX_HDR_SIZE
) {
460 ad
->ad_data
= talloc_realloc(ad
,
463 AD_XATTR_MAX_HDR_SIZE
);
464 if (ad
->ad_data
== NULL
) {
467 memset(ad
->ad_data
+ oldsize
,
469 AD_XATTR_MAX_HDR_SIZE
- oldsize
);
473 * First, let's calculate the start of the xattr data area which will be
474 * after the xattr header + header entries.
477 data_off
= ad_getentryoff(ad
, ADEID_FINDERI
);
478 data_off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
479 /* 2 bytes padding */
482 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
483 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
485 /* Align on 4 byte boundary */
486 data_off
= (data_off
+ 3) & ~3;
488 data_off
+= e
->adx_namelen
+ ADX_ENTRY_FIXED_SIZE
;
489 if (data_off
>= AD_XATTR_MAX_HDR_SIZE
) {
494 off
= ad_getentryoff(ad
, ADEID_FINDERI
);
495 off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
496 /* 2 bytes padding */
499 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
500 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
502 /* Align on 4 byte boundary */
503 off
= (off
+ 3) & ~3;
505 e
->adx_offset
= data_off
;
506 data_off
+= e
->adx_length
;
508 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
509 "adx_data_off [%zu]\n",
512 (size_t)e
->adx_namelen
,
514 (size_t)e
->adx_length
,
515 (size_t)e
->adx_offset
);
517 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
520 RSIVAL(ad
->ad_data
, off
, e
->adx_offset
);
523 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
526 RSIVAL(ad
->ad_data
, off
, e
->adx_length
);
529 if (off
+ 2 >= AD_XATTR_MAX_HDR_SIZE
) {
532 RSSVAL(ad
->ad_data
, off
, e
->adx_flags
);
535 if (off
+ 1 >= AD_XATTR_MAX_HDR_SIZE
) {
538 SCVAL(ad
->ad_data
, off
, e
->adx_namelen
);
541 if (off
+ e
->adx_namelen
>= AD_XATTR_MAX_HDR_SIZE
) {
544 memcpy(ad
->ad_data
+ off
, e
->adx_name
, e
->adx_namelen
);
545 off
+= e
->adx_namelen
;
548 h
->adx_data_start
= off
;
549 h
->adx_data_length
= talloc_get_size(ad
->adx_data
);
550 h
->adx_total_size
= h
->adx_data_start
+ h
->adx_data_length
;
552 if (talloc_get_size(ad
->ad_data
) < h
->adx_total_size
) {
553 ad
->ad_data
= talloc_realloc(ad
,
557 if (ad
->ad_data
== NULL
) {
562 memcpy(ad
->ad_data
+ h
->adx_data_start
,
568 h
->adx_total_size
- ad_getentryoff(ad
, ADEID_FINDERI
));
572 ad_getentryoff(ad
, ADEID_FINDERI
) +
573 ad_getentrylen(ad
, ADEID_FINDERI
));
575 memcpy(ad
->ad_data
+ ADEDOFF_FILLER
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
578 * Rewind, then update the header fields.
581 off
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
;
582 /* 2 bytes padding */
585 RSIVAL(ad
->ad_data
, off
, AD_XATTR_HDR_MAGIC
);
587 RSIVAL(ad
->ad_data
, off
, 0);
589 RSIVAL(ad
->ad_data
, off
, h
->adx_total_size
);
591 RSIVAL(ad
->ad_data
, off
, h
->adx_data_start
);
593 RSIVAL(ad
->ad_data
, off
, h
->adx_data_length
);
596 /* adx_reserved and adx_flags */
597 memset(ad
->ad_data
+ off
, 0, 3 * 4 + 2);
600 RSSVAL(ad
->ad_data
, off
, h
->adx_num_attrs
);
603 ok
= ad_pack_move_reso(handle
, ad
, fsp
);
605 DBG_ERR("Moving resourcefork of [%s] failed\n",
614 * Pack AppleDouble structure into data buffer
616 static bool ad_pack(struct vfs_handle_struct
*handle
,
626 bufsize
= talloc_get_size(ad
->ad_data
);
627 if (bufsize
< AD_DATASZ_DOT_UND
) {
628 DBG_ERR("bad buffer size [0x%" PRIx32
"]\n", bufsize
);
632 if (offset
+ ADEDLEN_MAGIC
< offset
||
633 offset
+ ADEDLEN_MAGIC
>= bufsize
) {
636 RSIVAL(ad
->ad_data
, offset
, ad
->ad_magic
);
637 offset
+= ADEDLEN_MAGIC
;
639 if (offset
+ ADEDLEN_VERSION
< offset
||
640 offset
+ ADEDLEN_VERSION
>= bufsize
) {
643 RSIVAL(ad
->ad_data
, offset
, ad
->ad_version
);
644 offset
+= ADEDLEN_VERSION
;
646 if (offset
+ ADEDLEN_FILLER
< offset
||
647 offset
+ ADEDLEN_FILLER
>= bufsize
) {
650 if (ad
->ad_type
== ADOUBLE_RSRC
) {
651 memcpy(ad
->ad_data
+ offset
, AD_FILLER_TAG
, ADEDLEN_FILLER
);
653 offset
+= ADEDLEN_FILLER
;
655 if (offset
+ ADEDLEN_NENTRIES
< offset
||
656 offset
+ ADEDLEN_NENTRIES
>= bufsize
) {
659 offset
+= ADEDLEN_NENTRIES
;
661 ok
= ad_pack_xattrs(handle
, ad
, fsp
);
666 for (eid
= 0, nent
= 0; eid
< ADEID_MAX
; eid
++) {
667 if (ad
->ad_eid
[eid
].ade_off
== 0) {
669 * ade_off is also used as indicator whether a
670 * specific entry is used or not
675 if (offset
+ AD_ENTRY_LEN_EID
< offset
||
676 offset
+ AD_ENTRY_LEN_EID
>= bufsize
) {
679 RSIVAL(ad
->ad_data
, offset
, AD_EID_DISK(eid
));
680 offset
+= AD_ENTRY_LEN_EID
;
682 if (offset
+ AD_ENTRY_LEN_OFF
< offset
||
683 offset
+ AD_ENTRY_LEN_OFF
>= bufsize
) {
686 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_off
);
687 offset
+= AD_ENTRY_LEN_OFF
;
689 if (offset
+ AD_ENTRY_LEN_LEN
< offset
||
690 offset
+ AD_ENTRY_LEN_LEN
>= bufsize
) {
693 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_len
);
694 offset
+= AD_ENTRY_LEN_LEN
;
699 if (ADEDOFF_NENTRIES
+ 2 >= bufsize
) {
702 RSSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
, nent
);
707 static bool ad_unpack_xattrs(struct adouble
*ad
)
709 struct ad_xattr_header
*h
= &ad
->adx_header
;
710 const char *p
= ad
->ad_data
;
714 if (ad_getentrylen(ad
, ADEID_FINDERI
) <= ADEDLEN_FINDERI
) {
718 /* 2 bytes padding */
719 hoff
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
+ 2;
721 h
->adx_magic
= RIVAL(p
, hoff
+ 0);
722 h
->adx_debug_tag
= RIVAL(p
, hoff
+ 4); /* Not used -> not checked */
723 h
->adx_total_size
= RIVAL(p
, hoff
+ 8);
724 h
->adx_data_start
= RIVAL(p
, hoff
+ 12);
725 h
->adx_data_length
= RIVAL(p
, hoff
+ 16);
726 h
->adx_flags
= RSVAL(p
, hoff
+ 32); /* Not used -> not checked */
727 h
->adx_num_attrs
= RSVAL(p
, hoff
+ 34);
729 if (h
->adx_magic
!= AD_XATTR_HDR_MAGIC
) {
730 DBG_ERR("Bad magic: 0x%" PRIx32
"\n", h
->adx_magic
);
734 if (h
->adx_total_size
> ad_getentryoff(ad
, ADEID_RFORK
)) {
735 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
738 if (h
->adx_total_size
> AD_XATTR_MAX_HDR_SIZE
) {
739 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
743 if (h
->adx_data_start
< (hoff
+ AD_XATTR_HDR_SIZE
)) {
744 DBG_ERR("Bad start: 0x%" PRIx32
"\n", h
->adx_data_start
);
748 if ((h
->adx_data_start
+ h
->adx_data_length
) < h
->adx_data_start
) {
749 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
752 if ((h
->adx_data_start
+ h
->adx_data_length
) >
753 ad
->adx_header
.adx_total_size
)
755 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
759 if (h
->adx_num_attrs
> AD_XATTR_MAX_ENTRIES
) {
760 DBG_ERR("Bad num xattrs: %" PRIu16
"\n", h
->adx_num_attrs
);
764 if (h
->adx_num_attrs
== 0) {
768 ad
->adx_entries
= talloc_zero_array(
769 ad
, struct ad_xattr_entry
, h
->adx_num_attrs
);
770 if (ad
->adx_entries
== NULL
) {
774 hoff
+= AD_XATTR_HDR_SIZE
;
776 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
777 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
779 hoff
= (hoff
+ 3) & ~3;
781 e
->adx_offset
= RIVAL(p
, hoff
+ 0);
782 e
->adx_length
= RIVAL(p
, hoff
+ 4);
783 e
->adx_flags
= RSVAL(p
, hoff
+ 8);
784 e
->adx_namelen
= *(p
+ hoff
+ 10);
786 if (e
->adx_offset
>= ad
->adx_header
.adx_total_size
) {
787 DBG_ERR("Bad adx_offset: %" PRIx32
"\n",
792 if ((e
->adx_offset
+ e
->adx_length
) < e
->adx_offset
) {
793 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
798 if ((e
->adx_offset
+ e
->adx_length
) >
799 ad
->adx_header
.adx_total_size
)
801 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
806 if (e
->adx_namelen
== 0) {
807 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
811 if ((hoff
+ 11 + e
->adx_namelen
) < hoff
+ 11) {
812 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
816 if ((hoff
+ 11 + e
->adx_namelen
) >
817 ad
->adx_header
.adx_data_start
)
819 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
824 e
->adx_name
= talloc_strndup(ad
->adx_entries
,
827 if (e
->adx_name
== NULL
) {
831 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
832 e
->adx_name
, e
->adx_offset
, e
->adx_length
);
833 dump_data(10, (uint8_t *)(ad
->ad_data
+ e
->adx_offset
),
836 hoff
+= 11 + e
->adx_namelen
;
843 * Unpack an AppleDouble blob into a struct adoble
845 static bool ad_unpack(struct adouble
*ad
, const size_t nentries
,
848 size_t bufsize
= talloc_get_size(ad
->ad_data
);
850 uint32_t eid
, len
, off
;
854 * The size of the buffer ad->ad_data is checked when read, so
855 * we wouldn't have to check our own offsets, a few extra
856 * checks won't hurt though. We have to check the offsets we
857 * read from the buffer anyway.
860 if (bufsize
< (AD_HEADER_LEN
+ (AD_ENTRY_LEN
* nentries
))) {
861 DEBUG(1, ("bad size\n"));
865 ad
->ad_magic
= RIVAL(ad
->ad_data
, 0);
866 ad
->ad_version
= RIVAL(ad
->ad_data
, ADEDOFF_VERSION
);
867 if ((ad
->ad_magic
!= AD_MAGIC
) || (ad
->ad_version
!= AD_VERSION
)) {
868 DEBUG(1, ("wrong magic or version\n"));
872 memcpy(ad
->ad_filler
, ad
->ad_data
+ ADEDOFF_FILLER
, ADEDLEN_FILLER
);
874 adentries
= RSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
);
875 if (adentries
!= nentries
) {
876 DEBUG(1, ("invalid number of entries: %zu\n",
881 /* now, read in the entry bits */
882 for (i
= 0; i
< adentries
; i
++) {
883 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
885 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
886 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
888 if (!eid
|| eid
>= ADEID_MAX
) {
889 DEBUG(1, ("bogus eid %d\n", eid
));
894 * All entries other than the resource fork are
895 * expected to be read into the ad_data buffer, so
896 * ensure the specified offset is within that bound
898 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
899 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
905 * All entries besides FinderInfo and resource fork
906 * must fit into the buffer. FinderInfo is special as
907 * it may be larger then the default 32 bytes (if it
908 * contains marshalled xattrs), but we will fixup that
909 * in ad_convert(). And the resource fork is never
910 * accessed directly by the ad_data buf (also see
911 * comment above) anyway.
913 if ((eid
!= ADEID_RFORK
) &&
914 (eid
!= ADEID_FINDERI
) &&
915 ((off
+ len
) > bufsize
)) {
916 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
922 * That would be obviously broken
924 if (off
> filesize
) {
925 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
931 * Check for any entry that has its end beyond the
934 if (off
+ len
< off
) {
935 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
936 ", len: %" PRIu32
"\n",
941 if (off
+ len
> filesize
) {
943 * If this is the resource fork entry, we fix
944 * up the length, for any other entry we bail
947 if (eid
!= ADEID_RFORK
) {
948 DEBUG(1, ("bogus eid %d: off: %" PRIu32
949 ", len: %" PRIu32
"\n",
955 * Fixup the resource fork entry by limiting
956 * the size to entryoffset - filesize.
958 len
= filesize
- off
;
959 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
960 ", len: %" PRIu32
"\n", off
, len
));
963 ad
->ad_eid
[eid
].ade_off
= off
;
964 ad
->ad_eid
[eid
].ade_len
= len
;
967 ok
= ad_unpack_xattrs(ad
);
975 static bool ad_convert_move_reso(vfs_handle_struct
*handle
,
977 const struct smb_filename
*smb_fname
)
985 rforklen
= ad_getentrylen(ad
, ADEID_RFORK
);
990 buf
= talloc_size(ad
, rforklen
);
993 * This allocates a buffer for reading the resource fork data in
994 * one big swoop. Resource forks won't be larger then, say, 64
995 * MB, I swear, so just doing the allocation with the talloc
996 * limit as safeguard seems safe.
998 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1003 rforkoff
= ad_getentryoff(ad
, ADEID_RFORK
);
1005 n
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1006 if (n
!= rforklen
) {
1007 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1008 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1012 rforkoff
= ADEDOFF_RFORK_DOT_UND
;
1014 n
= SMB_VFS_PWRITE(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1015 if (n
!= rforklen
) {
1016 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1017 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1021 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1023 ret
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1025 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1032 static bool ad_convert_xattr(vfs_handle_struct
*handle
,
1034 const struct smb_filename
*smb_fname
,
1035 const char *catia_mappings
,
1036 bool *converted_xattr
)
1038 static struct char_mappings
**string_replace_cmaps
= NULL
;
1040 int saved_errno
= 0;
1045 *converted_xattr
= false;
1047 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1051 if (string_replace_cmaps
== NULL
) {
1052 const char **mappings
= NULL
;
1054 mappings
= str_list_make_v3_const(
1055 talloc_tos(), catia_mappings
, NULL
);
1056 if (mappings
== NULL
) {
1059 string_replace_cmaps
= string_replace_init_map(
1060 handle
->conn
->sconn
, mappings
);
1061 TALLOC_FREE(mappings
);
1064 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1065 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1066 char *mapped_name
= NULL
;
1068 struct smb_filename
*stream_name
= NULL
;
1069 files_struct
*fsp
= NULL
;
1072 status
= string_replace_allocate(handle
->conn
,
1074 string_replace_cmaps
,
1077 vfs_translate_to_windows
);
1078 if (!NT_STATUS_IS_OK(status
) &&
1079 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1081 DBG_ERR("string_replace_allocate failed\n");
1087 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1089 if (mapped_name
== NULL
) {
1094 stream_name
= synthetic_smb_fname(talloc_tos(),
1095 smb_fname
->base_name
,
1100 TALLOC_FREE(mapped_name
);
1101 if (stream_name
== NULL
) {
1102 DBG_ERR("synthetic_smb_fname failed\n");
1107 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1109 status
= SMB_VFS_CREATE_FILE(
1110 handle
->conn
, /* conn */
1112 &handle
->conn
->cwd_fsp
, /* dirfsp */
1113 stream_name
, /* fname */
1114 FILE_GENERIC_WRITE
, /* access_mask */
1115 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1116 FILE_OPEN_IF
, /* create_disposition */
1117 0, /* create_options */
1118 0, /* file_attributes */
1119 INTERNAL_OPEN_ONLY
, /* oplock_request */
1121 0, /* allocation_size */
1122 0, /* private_flags */
1127 NULL
, NULL
); /* create context */
1128 TALLOC_FREE(stream_name
);
1129 if (!NT_STATUS_IS_OK(status
)) {
1130 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1135 nwritten
= SMB_VFS_PWRITE(fsp
,
1136 ad
->ad_data
+ e
->adx_offset
,
1139 if (nwritten
== -1) {
1140 DBG_ERR("SMB_VFS_PWRITE failed\n");
1141 saved_errno
= errno
;
1142 close_file(NULL
, fsp
, ERROR_CLOSE
);
1143 errno
= saved_errno
;
1148 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1149 if (!NT_STATUS_IS_OK(status
)) {
1156 ad
->adx_header
.adx_num_attrs
= 0;
1157 TALLOC_FREE(ad
->adx_entries
);
1159 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1161 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1163 DBG_ERR("ad_fset on [%s] failed: %s\n",
1164 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1169 ok
= ad_convert_move_reso(handle
, ad
, smb_fname
);
1174 *converted_xattr
= true;
1181 static bool ad_convert_finderinfo(vfs_handle_struct
*handle
,
1183 const struct smb_filename
*smb_fname
)
1188 struct smb_filename
*stream_name
= NULL
;
1189 files_struct
*fsp
= NULL
;
1193 int saved_errno
= 0;
1196 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1201 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1206 ai
= afpinfo_new(talloc_tos());
1211 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1213 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1214 if (aiblob
.data
== NULL
) {
1219 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1221 if (size
!= AFP_INFO_SIZE
) {
1225 stream_name
= synthetic_smb_fname(talloc_tos(),
1226 smb_fname
->base_name
,
1231 if (stream_name
== NULL
) {
1232 data_blob_free(&aiblob
);
1233 DBG_ERR("synthetic_smb_fname failed\n");
1237 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1239 status
= SMB_VFS_CREATE_FILE(
1240 handle
->conn
, /* conn */
1242 &handle
->conn
->cwd_fsp
, /* dirfsp */
1243 stream_name
, /* fname */
1244 FILE_GENERIC_WRITE
, /* access_mask */
1245 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1246 FILE_OPEN_IF
, /* create_disposition */
1247 0, /* create_options */
1248 0, /* file_attributes */
1249 INTERNAL_OPEN_ONLY
, /* oplock_request */
1251 0, /* allocation_size */
1252 0, /* private_flags */
1257 NULL
, NULL
); /* create context */
1258 TALLOC_FREE(stream_name
);
1259 if (!NT_STATUS_IS_OK(status
)) {
1260 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1264 nwritten
= SMB_VFS_PWRITE(fsp
,
1268 if (nwritten
== -1) {
1269 DBG_ERR("SMB_VFS_PWRITE failed\n");
1270 saved_errno
= errno
;
1271 close_file(NULL
, fsp
, ERROR_CLOSE
);
1272 errno
= saved_errno
;
1276 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1277 if (!NT_STATUS_IS_OK(status
)) {
1285 static bool ad_convert_truncate(vfs_handle_struct
*handle
,
1287 const struct smb_filename
*smb_fname
)
1292 newlen
= ADEDOFF_RFORK_DOT_UND
+ ad_getentrylen(ad
, ADEID_RFORK
);
1294 rc
= SMB_VFS_FTRUNCATE(ad
->ad_fsp
, newlen
);
1302 static bool ad_convert_blank_rfork(vfs_handle_struct
*handle
,
1307 size_t rforklen
= sizeof(empty_resourcefork
);
1315 if (!(flags
& AD_CONV_WIPE_BLANK
)) {
1319 if (ad_getentrylen(ad
, ADEID_RFORK
) != rforklen
) {
1323 nread
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, ADEDOFF_RFORK_DOT_UND
);
1324 if (nread
!= rforklen
) {
1325 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1326 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1330 cmp
= memcmp(buf
, empty_resourcefork
, rforklen
);
1335 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1337 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1339 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1347 static bool ad_convert_delete_adfile(vfs_handle_struct
*handle
,
1349 struct files_struct
*dirfsp
,
1350 const struct smb_filename
*smb_fname
,
1353 struct smb_filename
*ad_name
= NULL
;
1356 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1360 if (!(flags
& AD_CONV_DELETE
)) {
1364 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1369 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
1374 DBG_ERR("Unlinking [%s] failed: %s\n",
1375 smb_fname_str_dbg(ad_name
), strerror(errno
));
1376 TALLOC_FREE(ad_name
);
1380 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name
));
1381 TALLOC_FREE(ad_name
);
1387 * Convert from Apple's ._ file to Netatalk
1389 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1390 * bytes containing packed xattrs.
1392 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1395 int ad_convert(struct vfs_handle_struct
*handle
,
1396 struct files_struct
*dirfsp
,
1397 const struct smb_filename
*smb_fname
,
1398 const char *catia_mappings
,
1401 struct adouble
*ad
= NULL
;
1403 bool converted_xattr
= false;
1407 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
1412 ok
= ad_convert_xattr(handle
,
1422 ok
= ad_convert_blank_rfork(handle
, ad
, flags
, &blank
);
1428 if (converted_xattr
|| blank
) {
1429 ok
= ad_convert_truncate(handle
, ad
, smb_fname
);
1436 ok
= ad_convert_finderinfo(handle
, ad
, smb_fname
);
1438 DBG_ERR("Failed to convert [%s]\n",
1439 smb_fname_str_dbg(smb_fname
));
1444 ok
= ad_convert_delete_adfile(handle
,
1460 static bool ad_unconvert_open_ad(TALLOC_CTX
*mem_ctx
,
1461 struct vfs_handle_struct
*handle
,
1462 struct smb_filename
*smb_fname
,
1463 struct smb_filename
*adpath
,
1464 files_struct
**_fsp
)
1466 files_struct
*fsp
= NULL
;
1470 status
= SMB_VFS_CREATE_FILE(
1473 &handle
->conn
->cwd_fsp
, /* dirfsp */
1475 FILE_READ_DATA
|FILE_WRITE_DATA
,
1476 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1478 0, /* create_options */
1479 0, /* file_attributes */
1482 0, /* allocation_size */
1483 0, /* private_flags */
1488 NULL
, NULL
); /* create context */
1489 if (!NT_STATUS_IS_OK(status
)) {
1490 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1491 smb_fname_str_dbg(adpath
), nt_errstr(status
));
1495 if (fsp
->fsp_name
->st
.st_ex_uid
!= smb_fname
->st
.st_ex_uid
||
1496 fsp
->fsp_name
->st
.st_ex_gid
!= smb_fname
->st
.st_ex_gid
)
1498 ret
= SMB_VFS_FCHOWN(fsp
,
1499 smb_fname
->st
.st_ex_uid
,
1500 smb_fname
->st
.st_ex_gid
);
1502 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1503 fsp_str_dbg(fsp
), nt_errstr(status
));
1504 close_file(NULL
, fsp
, NORMAL_CLOSE
);
1513 static bool ad_unconvert_get_streams(struct vfs_handle_struct
*handle
,
1514 struct smb_filename
*smb_fname
,
1515 TALLOC_CTX
*mem_ctx
,
1516 unsigned int *num_streams
,
1517 struct stream_struct
**streams
)
1519 files_struct
*fsp
= NULL
;
1522 status
= SMB_VFS_CREATE_FILE(
1523 handle
->conn
, /* conn */
1525 &handle
->conn
->cwd_fsp
, /* dirfsp */
1526 smb_fname
, /* fname */
1527 FILE_READ_ATTRIBUTES
, /* access_mask */
1528 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
1530 FILE_OPEN
, /* create_disposition*/
1531 0, /* create_options */
1532 0, /* file_attributes */
1533 INTERNAL_OPEN_ONLY
, /* oplock_request */
1535 0, /* allocation_size */
1536 0, /* private_flags */
1541 NULL
, NULL
); /* create context */
1542 if (!NT_STATUS_IS_OK(status
)) {
1543 DBG_ERR("Opening [%s] failed: %s\n",
1544 smb_fname_str_dbg(smb_fname
),
1549 status
= vfs_streaminfo(handle
->conn
,
1555 if (!NT_STATUS_IS_OK(status
)) {
1556 close_file(NULL
, fsp
, NORMAL_CLOSE
);
1557 DBG_ERR("streaminfo on [%s] failed: %s\n",
1558 smb_fname_str_dbg(smb_fname
),
1563 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1564 if (!NT_STATUS_IS_OK(status
)) {
1565 DBG_ERR("close_file [%s] failed: %s\n",
1566 smb_fname_str_dbg(smb_fname
),
1574 struct ad_collect_state
{
1576 size_t adx_data_off
;
1577 char *rsrc_data_buf
;
1580 static bool ad_collect_one_stream(struct vfs_handle_struct
*handle
,
1581 struct char_mappings
**cmaps
,
1582 struct smb_filename
*smb_fname
,
1583 const struct stream_struct
*stream
,
1585 struct ad_collect_state
*state
)
1587 struct smb_filename
*sname
= NULL
;
1588 files_struct
*fsp
= NULL
;
1589 struct ad_xattr_entry
*e
= NULL
;
1590 char *mapped_name
= NULL
;
1598 sname
= synthetic_smb_fname(ad
,
1599 smb_fname
->base_name
,
1604 if (sname
== NULL
) {
1608 if (is_ntfs_default_stream_smb_fname(sname
)) {
1613 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname
));
1615 ret
= SMB_VFS_STAT(handle
->conn
, sname
);
1617 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname
));
1622 status
= SMB_VFS_CREATE_FILE(
1625 &handle
->conn
->cwd_fsp
, /* dirfsp */
1627 FILE_READ_DATA
|DELETE_ACCESS
,
1630 0, /* create_options */
1631 0, /* file_attributes */
1632 INTERNAL_OPEN_ONLY
, /* oplock_request */
1634 0, /* allocation_size */
1635 0, /* private_flags */
1640 NULL
, NULL
); /* create context */
1641 if (!NT_STATUS_IS_OK(status
)) {
1642 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1643 smb_fname_str_dbg(sname
));
1648 if (is_afpinfo_stream(stream
->name
)) {
1649 char buf
[AFP_INFO_SIZE
];
1651 if (stream
->size
!= AFP_INFO_SIZE
) {
1652 DBG_ERR("Bad size [%zd] on [%s]\n",
1653 (ssize_t
)stream
->size
,
1654 smb_fname_str_dbg(sname
));
1659 nread
= SMB_VFS_PREAD(fsp
, buf
, stream
->size
, 0);
1660 if (nread
!= AFP_INFO_SIZE
) {
1661 DBG_ERR("Bad size [%zd] on [%s]\n",
1662 (ssize_t
)stream
->size
,
1663 smb_fname_str_dbg(sname
));
1668 memcpy(ad
->ad_data
+ ADEDOFF_FINDERI_DOT_UND
,
1669 buf
+ AFP_OFF_FinderInfo
,
1672 ok
= set_delete_on_close(fsp
,
1674 fsp
->conn
->session_info
->security_token
,
1675 fsp
->conn
->session_info
->unix_token
);
1677 DBG_ERR("Deleting [%s] failed\n",
1678 smb_fname_str_dbg(sname
));
1686 if (is_afpresource_stream(stream
->name
)) {
1687 ad
->ad_rsrc_data
= talloc_size(ad
, stream
->size
);
1688 if (ad
->ad_rsrc_data
== NULL
) {
1693 nread
= SMB_VFS_PREAD(fsp
,
1697 if (nread
!= stream
->size
) {
1698 DBG_ERR("Bad size [%zd] on [%s]\n",
1699 (ssize_t
)stream
->size
,
1700 smb_fname_str_dbg(sname
));
1705 ad_setentrylen(ad
, ADEID_RFORK
, stream
->size
);
1707 if (!state
->have_adfile
) {
1709 * We have a resource *stream* but no AppleDouble
1710 * sidecar file, this means the share is configured with
1711 * fruit:resource=stream. So we should delete the
1714 ok
= set_delete_on_close(
1717 fsp
->conn
->session_info
->security_token
,
1718 fsp
->conn
->session_info
->unix_token
);
1720 DBG_ERR("Deleting [%s] failed\n",
1721 smb_fname_str_dbg(sname
));
1730 ad
->adx_entries
= talloc_realloc(ad
,
1732 struct ad_xattr_entry
,
1733 ad
->adx_header
.adx_num_attrs
+ 1);
1734 if (ad
->adx_entries
== NULL
) {
1739 e
= &ad
->adx_entries
[ad
->adx_header
.adx_num_attrs
];
1740 *e
= (struct ad_xattr_entry
) {
1741 .adx_length
= stream
->size
,
1743 e
->adx_name
= talloc_strdup(ad
, stream
->name
+ 1);
1744 if (e
->adx_name
== NULL
) {
1748 p
= strchr(e
->adx_name
, ':');
1753 status
= string_replace_allocate(handle
->conn
,
1758 vfs_translate_to_unix
);
1759 if (!NT_STATUS_IS_OK(status
) &&
1760 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1762 DBG_ERR("string_replace_allocate failed\n");
1767 e
->adx_name
= mapped_name
;
1768 e
->adx_namelen
= strlen(e
->adx_name
) + 1,
1770 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1771 ad
->adx_header
.adx_num_attrs
,
1773 (size_t)e
->adx_length
);
1775 ad
->adx_header
.adx_num_attrs
++;
1777 needed_size
= state
->adx_data_off
+ stream
->size
;
1778 if (needed_size
> talloc_get_size(ad
->adx_data
)) {
1779 ad
->adx_data
= talloc_realloc(ad
,
1783 if (ad
->adx_data
== NULL
) {
1789 nread
= SMB_VFS_PREAD(fsp
,
1790 ad
->adx_data
+ state
->adx_data_off
,
1793 if (nread
!= stream
->size
) {
1794 DBG_ERR("Bad size [%zd] on [%s]\n",
1795 (ssize_t
)stream
->size
,
1796 smb_fname_str_dbg(sname
));
1800 state
->adx_data_off
+= nread
;
1802 ok
= set_delete_on_close(fsp
,
1804 fsp
->conn
->session_info
->security_token
,
1805 fsp
->conn
->session_info
->unix_token
);
1807 DBG_ERR("Deleting [%s] failed\n",
1808 smb_fname_str_dbg(sname
));
1816 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1817 if (!NT_STATUS_IS_OK(status
)) {
1818 DBG_ERR("close_file [%s] failed: %s\n",
1819 smb_fname_str_dbg(smb_fname
),
1829 * Convert filesystem metadata to AppleDouble file
1831 bool ad_unconvert(TALLOC_CTX
*mem_ctx
,
1832 struct vfs_handle_struct
*handle
,
1833 const char *catia_mappings
,
1834 struct smb_filename
*smb_fname
,
1837 static struct char_mappings
**cmaps
= NULL
;
1838 TALLOC_CTX
*frame
= talloc_stackframe();
1839 struct ad_collect_state state
;
1840 struct stream_struct
*streams
= NULL
;
1841 struct smb_filename
*adpath
= NULL
;
1842 struct adouble
*ad
= NULL
;
1843 unsigned int num_streams
= 0;
1844 size_t to_convert
= 0;
1845 bool have_rsrc
= false;
1846 files_struct
*fsp
= NULL
;
1854 if (cmaps
== NULL
) {
1855 const char **mappings
= NULL
;
1857 mappings
= str_list_make_v3_const(
1858 frame
, catia_mappings
, NULL
);
1859 if (mappings
== NULL
) {
1863 cmaps
= string_replace_init_map(mem_ctx
, mappings
);
1864 TALLOC_FREE(mappings
);
1867 ok
= ad_unconvert_get_streams(handle
,
1876 for (i
= 0; i
< num_streams
; i
++) {
1877 if (strcasecmp_m(streams
[i
].name
, "::$DATA") == 0) {
1881 if (is_afpresource_stream(streams
[i
].name
)) {
1886 if (to_convert
== 0) {
1891 state
= (struct ad_collect_state
) {
1895 ret
= adouble_path(frame
, smb_fname
, &adpath
);
1901 ret
= SMB_VFS_STAT(handle
->conn
, adpath
);
1903 state
.have_adfile
= true;
1905 if (errno
!= ENOENT
) {
1909 state
.have_adfile
= false;
1912 if (to_convert
== 1 && have_rsrc
&& state
.have_adfile
) {
1914 * So we have just a single stream, the resource fork stream
1915 * from an AppleDouble file. Fine, that means there's nothing to
1922 ad
= ad_init(frame
, ADOUBLE_RSRC
);
1928 for (i
= 0; i
< num_streams
; i
++) {
1929 ok
= ad_collect_one_stream(handle
,
1940 ok
= ad_unconvert_open_ad(frame
, handle
, smb_fname
, adpath
, &fsp
);
1942 DBG_ERR("Failed to open adfile [%s]\n",
1943 smb_fname_str_dbg(smb_fname
));
1947 ret
= ad_fset(handle
, ad
, fsp
);
1958 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1959 if (!NT_STATUS_IS_OK(status
)) {
1960 DBG_ERR("close_file [%s] failed: %s\n",
1961 smb_fname_str_dbg(smb_fname
),
1971 * Read and parse Netatalk AppleDouble metadata xattr
1973 static ssize_t
ad_read_meta(vfs_handle_struct
*handle
,
1975 const struct smb_filename
*smb_fname
)
1981 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
1983 ealen
= SMB_VFS_GETXATTR(handle
->conn
, smb_fname
,
1984 AFPINFO_EA_NETATALK
, ad
->ad_data
,
1990 if (errno
== ENOATTR
) {
1996 DEBUG(2, ("error reading meta xattr: %s\n",
2002 if (ealen
!= AD_DATASZ_XATTR
) {
2003 DEBUG(2, ("bad size %zd\n", ealen
));
2009 /* Now parse entries */
2010 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
2012 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2018 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
2019 || !ad_getentryoff(ad
, ADEID_COMMENT
)
2020 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
2021 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
2022 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
2023 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
2024 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
2025 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
2026 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2033 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2034 smb_fname
->base_name
, rc
));
2038 if (errno
== EINVAL
) {
2040 (void)SMB_VFS_REMOVEXATTR(handle
->conn
,
2042 AFPINFO_EA_NETATALK
);
2050 static int ad_open_rsrc(vfs_handle_struct
*handle
,
2051 const struct smb_filename
*smb_fname
,
2054 files_struct
**_fsp
)
2057 struct smb_filename
*adp_smb_fname
= NULL
;
2058 files_struct
*fsp
= NULL
;
2059 uint32_t access_mask
;
2060 uint32_t share_access
;
2061 uint32_t create_disposition
;
2064 ret
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
2069 ret
= SMB_VFS_STAT(handle
->conn
, adp_smb_fname
);
2071 TALLOC_FREE(adp_smb_fname
);
2075 access_mask
= FILE_GENERIC_READ
;
2076 share_access
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
2077 create_disposition
= FILE_OPEN
;
2079 if (flags
& O_RDWR
) {
2080 access_mask
|= FILE_GENERIC_WRITE
;
2081 share_access
&= ~FILE_SHARE_WRITE
;
2084 status
= SMB_VFS_CREATE_FILE(
2085 handle
->conn
, /* conn */
2087 &handle
->conn
->cwd_fsp
, /* dirfsp */
2092 0, /* create_options */
2093 0, /* file_attributes */
2094 INTERNAL_OPEN_ONLY
, /* oplock_request */
2096 0, /* allocation_size */
2097 0, /* private_flags */
2102 NULL
, NULL
); /* create context */
2103 TALLOC_FREE(adp_smb_fname
);
2104 if (!NT_STATUS_IS_OK(status
)) {
2105 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2114 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2115 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2116 * for file IO on the ._ file.
2118 static int ad_open(vfs_handle_struct
*handle
,
2121 const struct smb_filename
*smb_fname
,
2127 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
2128 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2130 if (ad
->ad_type
== ADOUBLE_META
) {
2136 ad
->ad_opened
= false;
2140 ret
= ad_open_rsrc(handle
, smb_fname
, flags
, mode
, &ad
->ad_fsp
);
2144 ad
->ad_opened
= true;
2146 DBG_DEBUG("Path [%s] type [%s]\n",
2147 smb_fname
->base_name
,
2148 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2153 static ssize_t
ad_read_rsrc_adouble(vfs_handle_struct
*handle
,
2155 const struct smb_filename
*smb_fname
)
2162 ret
= SMB_VFS_NEXT_FSTAT(handle
, ad
->ad_fsp
, &ad
->ad_fsp
->fsp_name
->st
);
2164 DBG_ERR("fstat [%s] failed: %s\n",
2165 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
2169 to_read
= ad
->ad_fsp
->fsp_name
->st
.st_ex_size
;
2170 if (to_read
> AD_XATTR_MAX_HDR_SIZE
) {
2171 to_read
= AD_XATTR_MAX_HDR_SIZE
;
2174 len
= SMB_VFS_NEXT_PREAD(handle
,
2179 if (len
!= to_read
) {
2180 DBG_NOTICE("%s %s: bad size: %zd\n",
2181 smb_fname
->base_name
, strerror(errno
), len
);
2185 /* Now parse entries */
2188 ad
->ad_fsp
->fsp_name
->st
.st_ex_size
);
2190 DBG_ERR("invalid AppleDouble resource %s\n",
2191 smb_fname
->base_name
);
2196 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
2197 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
2198 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
))
2200 DBG_ERR("invalid AppleDouble resource %s\n",
2201 smb_fname
->base_name
);
2210 * Read and parse resource fork, either ._ AppleDouble file or xattr
2212 static ssize_t
ad_read_rsrc(vfs_handle_struct
*handle
,
2214 const struct smb_filename
*smb_fname
)
2216 return ad_read_rsrc_adouble(handle
, ad
, smb_fname
);
2220 * Read and unpack an AppleDouble metadata xattr or resource
2222 static ssize_t
ad_read(vfs_handle_struct
*handle
,
2224 const struct smb_filename
*smb_fname
)
2226 switch (ad
->ad_type
) {
2228 return ad_read_meta(handle
, ad
, smb_fname
);
2230 return ad_read_rsrc(handle
, ad
, smb_fname
);
2236 static int adouble_destructor(struct adouble
*ad
)
2240 if (!ad
->ad_opened
) {
2244 SMB_ASSERT(ad
->ad_fsp
!= NULL
);
2246 status
= close_file(NULL
, ad
->ad_fsp
, NORMAL_CLOSE
);
2247 if (!NT_STATUS_IS_OK(status
)) {
2248 DBG_ERR("Closing [%s] failed: %s\n",
2249 fsp_str_dbg(ad
->ad_fsp
), nt_errstr(status
));
2256 * Allocate a struct adouble without initialiing it
2258 * The struct is either hang of the fsp extension context or if fsp is
2261 * @param[in] ctx talloc context
2262 * @param[in] handle vfs handle
2263 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2265 * @return adouble handle
2267 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
,
2268 adouble_type_t type
)
2276 adsize
= AD_DATASZ_XATTR
;
2280 * AppleDouble ._ file case, optimize for fewer (but larger)
2283 * - without xattrs size of the header is exactly
2284 * AD_DATASZ_DOT_UND (82) bytes
2286 * - with embedded xattrs it can be larger, up to
2287 * AD_XATTR_MAX_HDR_SIZE
2289 * Larger headers are not supported, but this is a reasonable
2290 * limit that is also employed by the macOS client.
2292 * We used the largest possible size to be able to read the full
2293 * header with one IO.
2295 adsize
= AD_XATTR_MAX_HDR_SIZE
;
2301 ad
= talloc_zero(ctx
, struct adouble
);
2308 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
2309 if (ad
->ad_data
== NULL
) {
2316 ad
->ad_magic
= AD_MAGIC
;
2317 ad
->ad_version
= AD_VERSION
;
2319 talloc_set_destructor(ad
, adouble_destructor
);
2329 * Allocate and initialize a new struct adouble
2331 * @param[in] ctx talloc context
2332 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2334 * @return adouble handle, initialized
2336 struct adouble
*ad_init(TALLOC_CTX
*ctx
, adouble_type_t type
)
2339 const struct ad_entry_order
*eid
;
2340 struct adouble
*ad
= NULL
;
2341 time_t t
= time(NULL
);
2345 eid
= entry_order_meta_xattr
;
2348 eid
= entry_order_dot_und
;
2354 ad
= ad_alloc(ctx
, type
);
2360 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
2361 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
2365 /* put something sane in the date fields */
2366 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
2367 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
2368 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
2369 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
2377 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
2378 vfs_handle_struct
*handle
,
2380 const struct smb_filename
*smb_fname
,
2381 adouble_type_t type
)
2385 struct adouble
*ad
= NULL
;
2389 smb_fname
= fsp
->base_fsp
->fsp_name
;
2392 DEBUG(10, ("ad_get(%s) called for %s\n",
2393 type
== ADOUBLE_META
? "meta" : "rsrc",
2394 smb_fname
!= NULL
? smb_fname
->base_name
: "???"));
2396 ad
= ad_alloc(ctx
, type
);
2402 /* Try rw first so we can use the fd in ad_convert() */
2405 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2406 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
2408 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2411 DBG_DEBUG("ad_open [%s] error [%s]\n",
2412 smb_fname
->base_name
, strerror(errno
));
2417 len
= ad_read(handle
, ad
, smb_fname
);
2419 DEBUG(10, ("error reading AppleDouble for %s\n",
2420 smb_fname
->base_name
));
2426 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2427 type
== ADOUBLE_META
? "meta" : "rsrc",
2428 smb_fname
->base_name
, rc
));
2437 * Return AppleDouble data for a file
2439 * @param[in] ctx talloc context
2440 * @param[in] handle vfs handle
2441 * @param[in] smb_fname pathname to file or directory
2442 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2444 * @return talloced struct adouble or NULL on error
2446 struct adouble
*ad_get(TALLOC_CTX
*ctx
,
2447 vfs_handle_struct
*handle
,
2448 const struct smb_filename
*smb_fname
,
2449 adouble_type_t type
)
2451 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
2455 * Return AppleDouble data for a file
2457 * @param[in] ctx talloc context
2458 * @param[in] handle vfs handle
2459 * @param[in] fsp fsp to use for IO
2460 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2462 * @return talloced struct adouble or NULL on error
2464 struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
2465 files_struct
*fsp
, adouble_type_t type
)
2467 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
2471 * Set AppleDouble metadata on a file or directory
2473 * @param[in] ad adouble handle
2475 * @param[in] smb_fname pathname to file or directory
2477 * @return status code, 0 means success
2479 int ad_set(vfs_handle_struct
*handle
,
2481 const struct smb_filename
*smb_fname
)
2486 DBG_DEBUG("Path [%s]\n", smb_fname
->base_name
);
2488 if (ad
->ad_type
!= ADOUBLE_META
) {
2489 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2490 smb_fname
->base_name
);
2494 ok
= ad_pack(handle
, ad
, NULL
);
2499 ret
= SMB_VFS_SETXATTR(handle
->conn
,
2501 AFPINFO_EA_NETATALK
,
2503 AD_DATASZ_XATTR
, 0);
2505 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname
->base_name
, ret
);
2511 * Set AppleDouble metadata on a file or directory
2513 * @param[in] ad adouble handle
2514 * @param[in] fsp file handle
2516 * @return status code, 0 means success
2518 int ad_fset(struct vfs_handle_struct
*handle
,
2526 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2529 || (fsp
->fh
== NULL
)
2530 || (fsp
->fh
->fd
== -1))
2532 smb_panic("bad fsp");
2535 ok
= ad_pack(handle
, ad
, fsp
);
2540 switch (ad
->ad_type
) {
2542 rc
= SMB_VFS_NEXT_SETXATTR(handle
,
2544 AFPINFO_EA_NETATALK
,
2546 AD_DATASZ_XATTR
, 0);
2550 len
= SMB_VFS_NEXT_PWRITE(handle
,
2553 ad_getentryoff(ad
, ADEID_RFORK
),
2555 if (len
!= ad_getentryoff(ad
, ADEID_RFORK
)) {
2556 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp
), len
);
2566 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2571 bool is_adouble_file(const char *path
)
2573 const char *p
= NULL
;
2576 p
= strrchr(path
, '/');
2584 ADOUBLE_NAME_PREFIX
,
2585 strlen(ADOUBLE_NAME_PREFIX
));
2593 * Prepend "._" to a basename
2594 * Return a new struct smb_filename with stream_name == NULL.
2596 int adouble_path(TALLOC_CTX
*ctx
,
2597 const struct smb_filename
*smb_fname_in
,
2598 struct smb_filename
**pp_smb_fname_out
)
2602 struct smb_filename
*smb_fname
= cp_smb_filename(ctx
,
2605 if (smb_fname
== NULL
) {
2609 /* We need streamname to be NULL */
2610 TALLOC_FREE(smb_fname
->stream_name
);
2612 /* And we're replacing base_name. */
2613 TALLOC_FREE(smb_fname
->base_name
);
2615 SET_STAT_INVALID(smb_fname
->st
);
2617 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2619 TALLOC_FREE(smb_fname
);
2623 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2624 "%s/._%s", parent
, base
);
2625 if (smb_fname
->base_name
== NULL
) {
2626 TALLOC_FREE(smb_fname
);
2630 *pp_smb_fname_out
= smb_fname
;
2636 * Allocate and initialize an AfpInfo struct
2638 AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2640 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2644 ai
->afpi_Signature
= AFP_Signature
;
2645 ai
->afpi_Version
= AFP_Version
;
2646 ai
->afpi_BackupTime
= AD_DATE_START
;
2651 * Pack an AfpInfo struct into a buffer
2653 * Buffer size must be at least AFP_INFO_SIZE
2654 * Returns size of packed buffer
2656 ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2658 memset(buf
, 0, AFP_INFO_SIZE
);
2660 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2661 RSIVAL(buf
, 4, ai
->afpi_Version
);
2662 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2663 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2665 return AFP_INFO_SIZE
;
2669 * Unpack a buffer into a AfpInfo structure
2671 * Buffer size must be at least AFP_INFO_SIZE
2672 * Returns allocated AfpInfo struct
2674 AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
)
2676 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2681 ai
->afpi_Signature
= RIVAL(data
, 0);
2682 ai
->afpi_Version
= RIVAL(data
, 4);
2683 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2684 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2685 sizeof(ai
->afpi_FinderInfo
));
2687 if (ai
->afpi_Signature
!= AFP_Signature
2688 || ai
->afpi_Version
!= AFP_Version
) {
2689 DEBUG(1, ("Bad AfpInfo signature or version\n"));