2 * Samba AppleDouble helpers
4 * Copyright (C) Ralph Boehme, 2019
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "MacExtensions.h"
23 #include "string_replace.h"
24 #include "smbd/smbd.h"
25 #include "system/filesys.h"
26 #include "libcli/security/security.h"
27 #include "lib/util_macstreams.h"
31 "._" AppleDouble Header File Layout:
37 .-- AD ENTRY[0] Finder Info Entry (must be first)
38 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
40 | '-> FINDER INFO Fixed Size Data (32 Bytes)
41 | ~~~~~~~~~~~~~ 2 Bytes Padding
42 | EXT ATTR HDR Fixed Size Data (36 Bytes)
45 | ATTR ENTRY[1] --+--.
46 | ATTR ENTRY[2] --+--+--.
48 | ATTR ENTRY[N] --+--+--+--.
49 | ATTR DATA 0 <-' | | |
51 | ATTR DATA 1 <----' | |
53 | ATTR DATA 2 <-------' |
56 | ATTR DATA N <----------'
58 | ... Attribute Free Space
61 ... Variable Sized Data
65 /* Number of actually used entries */
66 #define ADEID_NUM_XATTR 8
67 #define ADEID_NUM_DOT_UND 2
68 #define ADEID_NUM_RSRC_XATTR 1
70 /* Sizes of relevant entry bits */
71 #define ADEDLEN_MAGIC 4
72 #define ADEDLEN_VERSION 4
73 #define ADEDLEN_FILLER 16
74 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
75 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
76 #define ADEDLEN_NENTRIES 2
77 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
78 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
79 #define AD_ENTRY_LEN_EID 4
80 #define AD_ENTRY_LEN_OFF 4
81 #define AD_ENTRY_LEN_LEN 4
82 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
85 #define ADEDOFF_MAGIC 0
86 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
87 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
88 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
90 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
91 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
92 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
93 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
94 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
96 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
97 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
98 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
99 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
101 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
102 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
103 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
105 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
106 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
107 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
108 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
109 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
110 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
112 #if AD_DATASZ_XATTR != 402
113 #error bad size for AD_DATASZ_XATTR
116 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
117 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
119 #if AD_DATASZ_DOT_UND != 82
120 #error bad size for AD_DATASZ_DOT_UND
123 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
124 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
125 #define AD_XATTR_HDR_SIZE 36
126 #define AD_XATTR_MAX_HDR_SIZE 65536
127 #define ADX_ENTRY_FIXED_SIZE (4+4+2+1)
130 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131 * representation as well as the on-disk format.
133 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
134 * the length of the FinderInfo entry is larger then 32 bytes. It is then
135 * preceeded with 2 bytes padding.
137 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
140 struct ad_xattr_header
{
141 uint32_t adx_magic
; /* ATTR_HDR_MAGIC */
142 uint32_t adx_debug_tag
; /* for debugging == file id of owning file */
143 uint32_t adx_total_size
; /* file offset of end of attribute header + entries + data */
144 uint32_t adx_data_start
; /* file offset to attribute data area */
145 uint32_t adx_data_length
; /* length of attribute data area */
146 uint32_t adx_reserved
[3];
148 uint16_t adx_num_attrs
;
151 /* On-disk entries are aligned on 4 byte boundaries */
152 struct ad_xattr_entry
{
153 uint32_t adx_offset
; /* file offset to data */
154 uint32_t adx_length
; /* size of attribute data */
156 uint8_t adx_namelen
; /* included the NULL terminator */
157 char *adx_name
; /* NULL-terminated UTF-8 name */
166 files_struct
*ad_fsp
;
168 adouble_type_t ad_type
;
171 uint8_t ad_filler
[ADEDLEN_FILLER
];
172 struct ad_entry ad_eid
[ADEID_MAX
];
175 struct ad_xattr_header adx_header
;
176 struct ad_xattr_entry
*adx_entries
;
180 struct ad_entry_order
{
181 uint32_t id
, offset
, len
;
184 /* Netatalk AppleDouble metadata xattr */
186 struct ad_entry_order entry_order_meta_xattr
[ADEID_NUM_XATTR
+ 1] = {
187 {ADEID_FINDERI
, ADEDOFF_FINDERI_XATTR
, ADEDLEN_FINDERI
},
188 {ADEID_COMMENT
, ADEDOFF_COMMENT_XATTR
, 0},
189 {ADEID_FILEDATESI
, ADEDOFF_FILEDATESI_XATTR
, ADEDLEN_FILEDATESI
},
190 {ADEID_AFPFILEI
, ADEDOFF_AFPFILEI_XATTR
, ADEDLEN_AFPFILEI
},
191 {ADEID_PRIVDEV
, ADEDOFF_PRIVDEV_XATTR
, 0},
192 {ADEID_PRIVINO
, ADEDOFF_PRIVINO_XATTR
, 0},
193 {ADEID_PRIVSYN
, ADEDOFF_PRIVSYN_XATTR
, 0},
194 {ADEID_PRIVID
, ADEDOFF_PRIVID_XATTR
, 0},
198 /* AppleDouble resource fork file (the ones prefixed by "._") */
200 struct ad_entry_order entry_order_dot_und
[ADEID_NUM_DOT_UND
+ 1] = {
201 {ADEID_FINDERI
, ADEDOFF_FINDERI_DOT_UND
, ADEDLEN_FINDERI
},
202 {ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
, 0},
206 /* Conversion from enumerated id to on-disk AppleDouble id */
207 #define AD_EID_DISK(a) (set_eid[a])
208 static const uint32_t set_eid
[] = {
209 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
210 AD_DEV
, AD_INO
, AD_SYN
, AD_ID
213 static char empty_resourcefork
[] = {
214 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
216 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
217 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
218 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
219 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
220 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
221 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
252 size_t ad_getentrylen(const struct adouble
*ad
, int eid
)
254 return ad
->ad_eid
[eid
].ade_len
;
257 size_t ad_getentryoff(const struct adouble
*ad
, int eid
)
259 return ad
->ad_eid
[eid
].ade_off
;
262 size_t ad_setentrylen(struct adouble
*ad
, int eid
, size_t len
)
264 return ad
->ad_eid
[eid
].ade_len
= len
;
267 size_t ad_setentryoff(struct adouble
*ad
, int eid
, size_t off
)
269 return ad
->ad_eid
[eid
].ade_off
= off
;
273 * Return a pointer to an AppleDouble entry
275 * Returns NULL if the entry is not present
277 char *ad_get_entry(const struct adouble
*ad
, int eid
)
279 off_t off
= ad_getentryoff(ad
, eid
);
280 size_t len
= ad_getentrylen(ad
, eid
);
282 if (off
== 0 || len
== 0) {
286 return ad
->ad_data
+ off
;
292 int ad_getdate(const struct adouble
*ad
, unsigned int dateoff
, uint32_t *date
)
294 bool xlate
= (dateoff
& AD_DATE_UNIX
);
297 dateoff
&= AD_DATE_MASK
;
298 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
303 if (dateoff
> AD_DATE_ACCESS
) {
307 memcpy(date
, p
+ dateoff
, sizeof(uint32_t));
310 *date
= AD_DATE_TO_UNIX(*date
);
318 int ad_setdate(struct adouble
*ad
, unsigned int dateoff
, uint32_t date
)
320 bool xlate
= (dateoff
& AD_DATE_UNIX
);
323 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
328 dateoff
&= AD_DATE_MASK
;
330 date
= AD_DATE_FROM_UNIX(date
);
333 if (dateoff
> AD_DATE_ACCESS
) {
337 memcpy(p
+ dateoff
, &date
, sizeof(date
));
344 * Map on-disk AppleDouble id to enumerated id
346 static uint32_t get_eid(uint32_t eid
)
354 return ADEID_PRIVDEV
;
356 return ADEID_PRIVINO
;
358 return ADEID_PRIVSYN
;
369 * Move resourcefork data in an AppleDouble file
371 * This is supposed to make room in an AppleDouble file by moving the
372 * resourcefork data behind the space required for packing additional xattr data
373 * in the extended FinderInfo entry.
375 * When we're called we're expecting an AppleDouble file with just two entries
376 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
377 * offset of ADEDOFF_RFORK_DOT_UND.
379 static bool ad_pack_move_reso(struct vfs_handle_struct
*handle
,
388 reso_len
= ad_getentrylen(ad
, ADEID_RFORK
);
389 reso_off
= ad_getentryoff(ad
, ADEID_RFORK
);
395 if (ad
->ad_rsrc_data
== NULL
) {
397 * This buffer is already set when converting a resourcefork
398 * stream from vfs_streams_depot backend via ad_unconvert(). It
399 * is NULL with vfs_streams_xattr where the resourcefork stream
400 * is stored in an AppleDouble sidecar file vy vfs_fruit.
402 ad
->ad_rsrc_data
= talloc_size(ad
, reso_len
);
403 if (ad
->ad_rsrc_data
== NULL
) {
407 n
= SMB_VFS_NEXT_PREAD(handle
,
411 ADEDOFF_RFORK_DOT_UND
);
413 DBG_ERR("Read on [%s] failed\n",
420 n
= SMB_VFS_NEXT_PWRITE(handle
,
426 DBG_ERR("Write on [%s] failed\n",
437 static bool ad_pack_xattrs(struct vfs_handle_struct
*handle
,
441 struct ad_xattr_header
*h
= &ad
->adx_header
;
448 if (ad
->adx_entries
== NULL
) {
449 /* No xattrs, nothing to pack */
454 DBG_ERR("fsp unexpectedly NULL\n");
458 oldsize
= talloc_get_size(ad
->ad_data
);
459 if (oldsize
< AD_XATTR_MAX_HDR_SIZE
) {
460 ad
->ad_data
= talloc_realloc(ad
,
463 AD_XATTR_MAX_HDR_SIZE
);
464 if (ad
->ad_data
== NULL
) {
467 memset(ad
->ad_data
+ oldsize
,
469 AD_XATTR_MAX_HDR_SIZE
- oldsize
);
473 * First, let's calculate the start of the xattr data area which will be
474 * after the xattr header + header entries.
477 data_off
= ad_getentryoff(ad
, ADEID_FINDERI
);
478 data_off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
479 /* 2 bytes padding */
482 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
483 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
485 /* Align on 4 byte boundary */
486 data_off
= (data_off
+ 3) & ~3;
488 data_off
+= e
->adx_namelen
+ ADX_ENTRY_FIXED_SIZE
;
489 if (data_off
>= AD_XATTR_MAX_HDR_SIZE
) {
494 off
= ad_getentryoff(ad
, ADEID_FINDERI
);
495 off
+= ADEDLEN_FINDERI
+ AD_XATTR_HDR_SIZE
;
496 /* 2 bytes padding */
499 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
500 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
502 /* Align on 4 byte boundary */
503 off
= (off
+ 3) & ~3;
505 e
->adx_offset
= data_off
;
506 data_off
+= e
->adx_length
;
508 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
509 "adx_data_off [%zu]\n",
512 (size_t)e
->adx_namelen
,
514 (size_t)e
->adx_length
,
515 (size_t)e
->adx_offset
);
517 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
520 RSIVAL(ad
->ad_data
, off
, e
->adx_offset
);
523 if (off
+ 4 >= AD_XATTR_MAX_HDR_SIZE
) {
526 RSIVAL(ad
->ad_data
, off
, e
->adx_length
);
529 if (off
+ 2 >= AD_XATTR_MAX_HDR_SIZE
) {
532 RSSVAL(ad
->ad_data
, off
, e
->adx_flags
);
535 if (off
+ 1 >= AD_XATTR_MAX_HDR_SIZE
) {
538 SCVAL(ad
->ad_data
, off
, e
->adx_namelen
);
541 if (off
+ e
->adx_namelen
>= AD_XATTR_MAX_HDR_SIZE
) {
544 memcpy(ad
->ad_data
+ off
, e
->adx_name
, e
->adx_namelen
);
545 off
+= e
->adx_namelen
;
548 h
->adx_data_start
= off
;
549 h
->adx_data_length
= talloc_get_size(ad
->adx_data
);
550 h
->adx_total_size
= h
->adx_data_start
+ h
->adx_data_length
;
552 if (talloc_get_size(ad
->ad_data
) < h
->adx_total_size
) {
553 ad
->ad_data
= talloc_realloc(ad
,
557 if (ad
->ad_data
== NULL
) {
562 memcpy(ad
->ad_data
+ h
->adx_data_start
,
568 h
->adx_total_size
- ad_getentryoff(ad
, ADEID_FINDERI
));
572 ad_getentryoff(ad
, ADEID_FINDERI
) +
573 ad_getentrylen(ad
, ADEID_FINDERI
));
575 memcpy(ad
->ad_data
+ ADEDOFF_FILLER
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
578 * Rewind, then update the header fields.
581 off
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
;
582 /* 2 bytes padding */
585 RSIVAL(ad
->ad_data
, off
, AD_XATTR_HDR_MAGIC
);
587 RSIVAL(ad
->ad_data
, off
, 0);
589 RSIVAL(ad
->ad_data
, off
, h
->adx_total_size
);
591 RSIVAL(ad
->ad_data
, off
, h
->adx_data_start
);
593 RSIVAL(ad
->ad_data
, off
, h
->adx_data_length
);
596 /* adx_reserved and adx_flags */
597 memset(ad
->ad_data
+ off
, 0, 3 * 4 + 2);
600 RSSVAL(ad
->ad_data
, off
, h
->adx_num_attrs
);
603 ok
= ad_pack_move_reso(handle
, ad
, fsp
);
605 DBG_ERR("Moving resourcefork of [%s] failed\n",
614 * Pack AppleDouble structure into data buffer
616 static bool ad_pack(struct vfs_handle_struct
*handle
,
626 bufsize
= talloc_get_size(ad
->ad_data
);
627 if (bufsize
< AD_DATASZ_DOT_UND
) {
628 DBG_ERR("bad buffer size [0x%" PRIx32
"]\n", bufsize
);
632 if (offset
+ ADEDLEN_MAGIC
< offset
||
633 offset
+ ADEDLEN_MAGIC
>= bufsize
) {
636 RSIVAL(ad
->ad_data
, offset
, ad
->ad_magic
);
637 offset
+= ADEDLEN_MAGIC
;
639 if (offset
+ ADEDLEN_VERSION
< offset
||
640 offset
+ ADEDLEN_VERSION
>= bufsize
) {
643 RSIVAL(ad
->ad_data
, offset
, ad
->ad_version
);
644 offset
+= ADEDLEN_VERSION
;
646 if (offset
+ ADEDLEN_FILLER
< offset
||
647 offset
+ ADEDLEN_FILLER
>= bufsize
) {
650 if (ad
->ad_type
== ADOUBLE_RSRC
) {
651 memcpy(ad
->ad_data
+ offset
, AD_FILLER_TAG
, ADEDLEN_FILLER
);
653 offset
+= ADEDLEN_FILLER
;
655 if (offset
+ ADEDLEN_NENTRIES
< offset
||
656 offset
+ ADEDLEN_NENTRIES
>= bufsize
) {
659 offset
+= ADEDLEN_NENTRIES
;
661 ok
= ad_pack_xattrs(handle
, ad
, fsp
);
666 for (eid
= 0, nent
= 0; eid
< ADEID_MAX
; eid
++) {
667 if (ad
->ad_eid
[eid
].ade_off
== 0) {
669 * ade_off is also used as indicator whether a
670 * specific entry is used or not
675 if (offset
+ AD_ENTRY_LEN_EID
< offset
||
676 offset
+ AD_ENTRY_LEN_EID
>= bufsize
) {
679 RSIVAL(ad
->ad_data
, offset
, AD_EID_DISK(eid
));
680 offset
+= AD_ENTRY_LEN_EID
;
682 if (offset
+ AD_ENTRY_LEN_OFF
< offset
||
683 offset
+ AD_ENTRY_LEN_OFF
>= bufsize
) {
686 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_off
);
687 offset
+= AD_ENTRY_LEN_OFF
;
689 if (offset
+ AD_ENTRY_LEN_LEN
< offset
||
690 offset
+ AD_ENTRY_LEN_LEN
>= bufsize
) {
693 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_len
);
694 offset
+= AD_ENTRY_LEN_LEN
;
699 if (ADEDOFF_NENTRIES
+ 2 >= bufsize
) {
702 RSSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
, nent
);
707 static bool ad_unpack_xattrs(struct adouble
*ad
)
709 struct ad_xattr_header
*h
= &ad
->adx_header
;
710 const char *p
= ad
->ad_data
;
714 if (ad_getentrylen(ad
, ADEID_FINDERI
) <= ADEDLEN_FINDERI
) {
718 /* 2 bytes padding */
719 hoff
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
+ 2;
721 h
->adx_magic
= RIVAL(p
, hoff
+ 0);
722 h
->adx_debug_tag
= RIVAL(p
, hoff
+ 4); /* Not used -> not checked */
723 h
->adx_total_size
= RIVAL(p
, hoff
+ 8);
724 h
->adx_data_start
= RIVAL(p
, hoff
+ 12);
725 h
->adx_data_length
= RIVAL(p
, hoff
+ 16);
726 h
->adx_flags
= RSVAL(p
, hoff
+ 32); /* Not used -> not checked */
727 h
->adx_num_attrs
= RSVAL(p
, hoff
+ 34);
729 if (h
->adx_magic
!= AD_XATTR_HDR_MAGIC
) {
730 DBG_ERR("Bad magic: 0x%" PRIx32
"\n", h
->adx_magic
);
734 if (h
->adx_total_size
> ad_getentryoff(ad
, ADEID_RFORK
)) {
735 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
738 if (h
->adx_total_size
> AD_XATTR_MAX_HDR_SIZE
) {
739 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
743 if (h
->adx_data_start
< (hoff
+ AD_XATTR_HDR_SIZE
)) {
744 DBG_ERR("Bad start: 0x%" PRIx32
"\n", h
->adx_data_start
);
748 if ((h
->adx_data_start
+ h
->adx_data_length
) < h
->adx_data_start
) {
749 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
752 if ((h
->adx_data_start
+ h
->adx_data_length
) >
753 ad
->adx_header
.adx_total_size
)
755 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
759 if (h
->adx_num_attrs
> AD_XATTR_MAX_ENTRIES
) {
760 DBG_ERR("Bad num xattrs: %" PRIu16
"\n", h
->adx_num_attrs
);
764 if (h
->adx_num_attrs
== 0) {
768 ad
->adx_entries
= talloc_zero_array(
769 ad
, struct ad_xattr_entry
, h
->adx_num_attrs
);
770 if (ad
->adx_entries
== NULL
) {
774 hoff
+= AD_XATTR_HDR_SIZE
;
776 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
777 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
779 hoff
= (hoff
+ 3) & ~3;
781 e
->adx_offset
= RIVAL(p
, hoff
+ 0);
782 e
->adx_length
= RIVAL(p
, hoff
+ 4);
783 e
->adx_flags
= RSVAL(p
, hoff
+ 8);
784 e
->adx_namelen
= *(p
+ hoff
+ 10);
786 if (e
->adx_offset
>= ad
->adx_header
.adx_total_size
) {
787 DBG_ERR("Bad adx_offset: %" PRIx32
"\n",
792 if ((e
->adx_offset
+ e
->adx_length
) < e
->adx_offset
) {
793 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
798 if ((e
->adx_offset
+ e
->adx_length
) >
799 ad
->adx_header
.adx_total_size
)
801 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
806 if (e
->adx_namelen
== 0) {
807 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
811 if ((hoff
+ 11 + e
->adx_namelen
) < hoff
+ 11) {
812 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
816 if ((hoff
+ 11 + e
->adx_namelen
) >
817 ad
->adx_header
.adx_data_start
)
819 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
824 e
->adx_name
= talloc_strndup(ad
->adx_entries
,
827 if (e
->adx_name
== NULL
) {
831 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
832 e
->adx_name
, e
->adx_offset
, e
->adx_length
);
833 dump_data(10, (uint8_t *)(ad
->ad_data
+ e
->adx_offset
),
836 hoff
+= 11 + e
->adx_namelen
;
843 * Unpack an AppleDouble blob into a struct adoble
845 static bool ad_unpack(struct adouble
*ad
, const size_t nentries
,
848 size_t bufsize
= talloc_get_size(ad
->ad_data
);
850 uint32_t eid
, len
, off
;
854 * The size of the buffer ad->ad_data is checked when read, so
855 * we wouldn't have to check our own offsets, a few extra
856 * checks won't hurt though. We have to check the offsets we
857 * read from the buffer anyway.
860 if (bufsize
< (AD_HEADER_LEN
+ (AD_ENTRY_LEN
* nentries
))) {
861 DEBUG(1, ("bad size\n"));
865 ad
->ad_magic
= RIVAL(ad
->ad_data
, 0);
866 ad
->ad_version
= RIVAL(ad
->ad_data
, ADEDOFF_VERSION
);
867 if ((ad
->ad_magic
!= AD_MAGIC
) || (ad
->ad_version
!= AD_VERSION
)) {
868 DEBUG(1, ("wrong magic or version\n"));
872 memcpy(ad
->ad_filler
, ad
->ad_data
+ ADEDOFF_FILLER
, ADEDLEN_FILLER
);
874 adentries
= RSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
);
875 if (adentries
!= nentries
) {
876 DEBUG(1, ("invalid number of entries: %zu\n",
881 /* now, read in the entry bits */
882 for (i
= 0; i
< adentries
; i
++) {
883 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
885 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
886 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
888 if (!eid
|| eid
>= ADEID_MAX
) {
889 DEBUG(1, ("bogus eid %d\n", eid
));
894 * All entries other than the resource fork are
895 * expected to be read into the ad_data buffer, so
896 * ensure the specified offset is within that bound
898 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
899 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
905 * All entries besides FinderInfo and resource fork
906 * must fit into the buffer. FinderInfo is special as
907 * it may be larger then the default 32 bytes (if it
908 * contains marshalled xattrs), but we will fixup that
909 * in ad_convert(). And the resource fork is never
910 * accessed directly by the ad_data buf (also see
911 * comment above) anyway.
913 if ((eid
!= ADEID_RFORK
) &&
914 (eid
!= ADEID_FINDERI
) &&
915 ((off
+ len
) > bufsize
)) {
916 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
922 * That would be obviously broken
924 if (off
> filesize
) {
925 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
931 * Check for any entry that has its end beyond the
934 if (off
+ len
< off
) {
935 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
936 ", len: %" PRIu32
"\n",
941 if (off
+ len
> filesize
) {
943 * If this is the resource fork entry, we fix
944 * up the length, for any other entry we bail
947 if (eid
!= ADEID_RFORK
) {
948 DEBUG(1, ("bogus eid %d: off: %" PRIu32
949 ", len: %" PRIu32
"\n",
955 * Fixup the resource fork entry by limiting
956 * the size to entryoffset - filesize.
958 len
= filesize
- off
;
959 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
960 ", len: %" PRIu32
"\n", off
, len
));
963 ad
->ad_eid
[eid
].ade_off
= off
;
964 ad
->ad_eid
[eid
].ade_len
= len
;
967 ok
= ad_unpack_xattrs(ad
);
975 static bool ad_convert_move_reso(vfs_handle_struct
*handle
,
977 const struct smb_filename
*smb_fname
)
985 rforklen
= ad_getentrylen(ad
, ADEID_RFORK
);
990 buf
= talloc_size(ad
, rforklen
);
993 * This allocates a buffer for reading the resource fork data in
994 * one big swoop. Resource forks won't be larger then, say, 64
995 * MB, I swear, so just doing the allocation with the talloc
996 * limit as safeguard seems safe.
998 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1003 rforkoff
= ad_getentryoff(ad
, ADEID_RFORK
);
1005 n
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1006 if (n
!= rforklen
) {
1007 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1008 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1012 rforkoff
= ADEDOFF_RFORK_DOT_UND
;
1014 n
= SMB_VFS_PWRITE(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1015 if (n
!= rforklen
) {
1016 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1017 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1021 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1023 ret
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1025 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1032 static bool ad_convert_xattr(vfs_handle_struct
*handle
,
1034 const struct smb_filename
*smb_fname
,
1035 const char *catia_mappings
,
1036 bool *converted_xattr
)
1038 static struct char_mappings
**string_replace_cmaps
= NULL
;
1040 int saved_errno
= 0;
1045 *converted_xattr
= false;
1047 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1051 if (string_replace_cmaps
== NULL
) {
1052 const char **mappings
= NULL
;
1054 mappings
= str_list_make_v3_const(
1055 talloc_tos(), catia_mappings
, NULL
);
1056 if (mappings
== NULL
) {
1059 string_replace_cmaps
= string_replace_init_map(
1060 handle
->conn
->sconn
, mappings
);
1061 TALLOC_FREE(mappings
);
1064 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1065 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1066 char *mapped_name
= NULL
;
1068 struct smb_filename
*stream_name
= NULL
;
1069 files_struct
*fsp
= NULL
;
1072 status
= string_replace_allocate(handle
->conn
,
1074 string_replace_cmaps
,
1077 vfs_translate_to_windows
);
1078 if (!NT_STATUS_IS_OK(status
) &&
1079 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1081 DBG_ERR("string_replace_allocate failed\n");
1087 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1089 if (mapped_name
== NULL
) {
1094 stream_name
= synthetic_smb_fname(talloc_tos(),
1095 smb_fname
->base_name
,
1099 TALLOC_FREE(mapped_name
);
1100 if (stream_name
== NULL
) {
1101 DBG_ERR("synthetic_smb_fname failed\n");
1106 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1108 status
= SMB_VFS_CREATE_FILE(
1109 handle
->conn
, /* conn */
1111 0, /* root_dir_fid */
1112 stream_name
, /* fname */
1113 FILE_GENERIC_WRITE
, /* access_mask */
1114 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1115 FILE_OPEN_IF
, /* create_disposition */
1116 0, /* create_options */
1117 0, /* file_attributes */
1118 INTERNAL_OPEN_ONLY
, /* oplock_request */
1120 0, /* allocation_size */
1121 0, /* private_flags */
1126 NULL
, NULL
); /* create context */
1127 TALLOC_FREE(stream_name
);
1128 if (!NT_STATUS_IS_OK(status
)) {
1129 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1134 nwritten
= SMB_VFS_PWRITE(fsp
,
1135 ad
->ad_data
+ e
->adx_offset
,
1138 if (nwritten
== -1) {
1139 DBG_ERR("SMB_VFS_PWRITE failed\n");
1140 saved_errno
= errno
;
1141 close_file(NULL
, fsp
, ERROR_CLOSE
);
1142 errno
= saved_errno
;
1147 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1148 if (!NT_STATUS_IS_OK(status
)) {
1155 ad
->adx_header
.adx_num_attrs
= 0;
1156 TALLOC_FREE(ad
->adx_entries
);
1158 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1160 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1162 DBG_ERR("ad_fset on [%s] failed: %s\n",
1163 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1168 ok
= ad_convert_move_reso(handle
, ad
, smb_fname
);
1173 *converted_xattr
= true;
1180 static bool ad_convert_finderinfo(vfs_handle_struct
*handle
,
1182 const struct smb_filename
*smb_fname
)
1187 struct smb_filename
*stream_name
= NULL
;
1188 files_struct
*fsp
= NULL
;
1192 int saved_errno
= 0;
1195 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1200 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1205 ai
= afpinfo_new(talloc_tos());
1210 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1212 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1213 if (aiblob
.data
== NULL
) {
1218 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1220 if (size
!= AFP_INFO_SIZE
) {
1224 stream_name
= synthetic_smb_fname(talloc_tos(),
1225 smb_fname
->base_name
,
1229 if (stream_name
== NULL
) {
1230 data_blob_free(&aiblob
);
1231 DBG_ERR("synthetic_smb_fname failed\n");
1235 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1237 status
= SMB_VFS_CREATE_FILE(
1238 handle
->conn
, /* conn */
1240 0, /* root_dir_fid */
1241 stream_name
, /* fname */
1242 FILE_GENERIC_WRITE
, /* access_mask */
1243 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1244 FILE_OPEN_IF
, /* create_disposition */
1245 0, /* create_options */
1246 0, /* file_attributes */
1247 INTERNAL_OPEN_ONLY
, /* oplock_request */
1249 0, /* allocation_size */
1250 0, /* private_flags */
1255 NULL
, NULL
); /* create context */
1256 TALLOC_FREE(stream_name
);
1257 if (!NT_STATUS_IS_OK(status
)) {
1258 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1262 nwritten
= SMB_VFS_PWRITE(fsp
,
1266 if (nwritten
== -1) {
1267 DBG_ERR("SMB_VFS_PWRITE failed\n");
1268 saved_errno
= errno
;
1269 close_file(NULL
, fsp
, ERROR_CLOSE
);
1270 errno
= saved_errno
;
1274 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1275 if (!NT_STATUS_IS_OK(status
)) {
1283 static bool ad_convert_truncate(vfs_handle_struct
*handle
,
1285 const struct smb_filename
*smb_fname
)
1290 newlen
= ADEDOFF_RFORK_DOT_UND
+ ad_getentrylen(ad
, ADEID_RFORK
);
1292 rc
= SMB_VFS_FTRUNCATE(ad
->ad_fsp
, newlen
);
1300 static bool ad_convert_blank_rfork(vfs_handle_struct
*handle
,
1305 size_t rforklen
= sizeof(empty_resourcefork
);
1313 if (!(flags
& AD_CONV_WIPE_BLANK
)) {
1317 if (ad_getentrylen(ad
, ADEID_RFORK
) != rforklen
) {
1321 nread
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, ADEDOFF_RFORK_DOT_UND
);
1322 if (nread
!= rforklen
) {
1323 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1324 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1328 cmp
= memcmp(buf
, empty_resourcefork
, rforklen
);
1333 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1335 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1337 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1345 static bool ad_convert_delete_adfile(vfs_handle_struct
*handle
,
1347 struct files_struct
*dirfsp
,
1348 const struct smb_filename
*smb_fname
,
1351 struct smb_filename
*ad_name
= NULL
;
1354 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1358 if (!(flags
& AD_CONV_DELETE
)) {
1362 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1367 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
1372 DBG_ERR("Unlinking [%s] failed: %s\n",
1373 smb_fname_str_dbg(ad_name
), strerror(errno
));
1374 TALLOC_FREE(ad_name
);
1378 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name
));
1379 TALLOC_FREE(ad_name
);
1385 * Convert from Apple's ._ file to Netatalk
1387 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1388 * bytes containing packed xattrs.
1390 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1393 int ad_convert(struct vfs_handle_struct
*handle
,
1394 struct files_struct
*dirfsp
,
1395 const struct smb_filename
*smb_fname
,
1396 const char *catia_mappings
,
1399 struct adouble
*ad
= NULL
;
1401 bool converted_xattr
= false;
1405 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
1410 ok
= ad_convert_xattr(handle
,
1420 ok
= ad_convert_blank_rfork(handle
, ad
, flags
, &blank
);
1426 if (converted_xattr
|| blank
) {
1427 ok
= ad_convert_truncate(handle
, ad
, smb_fname
);
1434 ok
= ad_convert_finderinfo(handle
, ad
, smb_fname
);
1436 DBG_ERR("Failed to convert [%s]\n",
1437 smb_fname_str_dbg(smb_fname
));
1442 ok
= ad_convert_delete_adfile(handle
,
1458 static bool ad_unconvert_open_ad(TALLOC_CTX
*mem_ctx
,
1459 struct vfs_handle_struct
*handle
,
1460 struct smb_filename
*smb_fname
,
1461 struct smb_filename
*adpath
,
1462 files_struct
**_fsp
)
1464 files_struct
*fsp
= NULL
;
1468 status
= SMB_VFS_CREATE_FILE(
1471 0, /* root_dir_fid */
1473 FILE_READ_DATA
|FILE_WRITE_DATA
,
1474 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1476 0, /* create_options */
1477 0, /* file_attributes */
1480 0, /* allocation_size */
1481 0, /* private_flags */
1486 NULL
, NULL
); /* create context */
1487 if (!NT_STATUS_IS_OK(status
)) {
1488 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1489 smb_fname_str_dbg(adpath
), nt_errstr(status
));
1493 if (fsp
->fsp_name
->st
.st_ex_uid
!= smb_fname
->st
.st_ex_uid
||
1494 fsp
->fsp_name
->st
.st_ex_gid
!= smb_fname
->st
.st_ex_gid
)
1496 ret
= SMB_VFS_FCHOWN(fsp
,
1497 smb_fname
->st
.st_ex_uid
,
1498 smb_fname
->st
.st_ex_gid
);
1500 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1501 fsp_str_dbg(fsp
), nt_errstr(status
));
1502 close_file(NULL
, fsp
, NORMAL_CLOSE
);
1511 static bool ad_unconvert_get_streams(struct vfs_handle_struct
*handle
,
1512 struct smb_filename
*smb_fname
,
1513 TALLOC_CTX
*mem_ctx
,
1514 unsigned int *num_streams
,
1515 struct stream_struct
**streams
)
1517 files_struct
*fsp
= NULL
;
1520 status
= SMB_VFS_CREATE_FILE(
1521 handle
->conn
, /* conn */
1523 0, /* root_dir_fid */
1524 smb_fname
, /* fname */
1525 FILE_READ_ATTRIBUTES
, /* access_mask */
1526 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
1528 FILE_OPEN
, /* create_disposition*/
1529 0, /* create_options */
1530 0, /* file_attributes */
1531 INTERNAL_OPEN_ONLY
, /* oplock_request */
1533 0, /* allocation_size */
1534 0, /* private_flags */
1539 NULL
, NULL
); /* create context */
1540 if (!NT_STATUS_IS_OK(status
)) {
1541 DBG_ERR("Opening [%s] failed: %s\n",
1542 smb_fname_str_dbg(smb_fname
),
1547 status
= vfs_streaminfo(handle
->conn
,
1553 if (!NT_STATUS_IS_OK(status
)) {
1554 close_file(NULL
, fsp
, NORMAL_CLOSE
);
1555 DBG_ERR("streaminfo on [%s] failed: %s\n",
1556 smb_fname_str_dbg(smb_fname
),
1561 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1562 if (!NT_STATUS_IS_OK(status
)) {
1563 DBG_ERR("close_file [%s] failed: %s\n",
1564 smb_fname_str_dbg(smb_fname
),
1572 struct ad_collect_state
{
1574 size_t adx_data_off
;
1575 char *rsrc_data_buf
;
1578 static bool ad_collect_one_stream(struct vfs_handle_struct
*handle
,
1579 struct char_mappings
**cmaps
,
1580 struct smb_filename
*smb_fname
,
1581 const struct stream_struct
*stream
,
1583 struct ad_collect_state
*state
)
1585 struct smb_filename
*sname
= NULL
;
1586 files_struct
*fsp
= NULL
;
1587 struct ad_xattr_entry
*e
= NULL
;
1588 char *mapped_name
= NULL
;
1596 sname
= synthetic_smb_fname(ad
,
1597 smb_fname
->base_name
,
1601 if (sname
== NULL
) {
1605 if (is_ntfs_default_stream_smb_fname(sname
)) {
1610 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname
));
1612 ret
= SMB_VFS_STAT(handle
->conn
, sname
);
1614 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname
));
1619 status
= SMB_VFS_CREATE_FILE(
1622 0, /* root_dir_fid */
1624 FILE_READ_DATA
|DELETE_ACCESS
,
1627 0, /* create_options */
1628 0, /* file_attributes */
1629 INTERNAL_OPEN_ONLY
, /* oplock_request */
1631 0, /* allocation_size */
1632 0, /* private_flags */
1637 NULL
, NULL
); /* create context */
1638 if (!NT_STATUS_IS_OK(status
)) {
1639 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1640 smb_fname_str_dbg(sname
));
1645 if (is_afpinfo_stream(stream
->name
)) {
1646 char buf
[AFP_INFO_SIZE
];
1648 if (stream
->size
!= AFP_INFO_SIZE
) {
1649 DBG_ERR("Bad size [%zd] on [%s]\n",
1650 (ssize_t
)stream
->size
,
1651 smb_fname_str_dbg(sname
));
1656 nread
= SMB_VFS_PREAD(fsp
, buf
, stream
->size
, 0);
1657 if (nread
!= AFP_INFO_SIZE
) {
1658 DBG_ERR("Bad size [%zd] on [%s]\n",
1659 (ssize_t
)stream
->size
,
1660 smb_fname_str_dbg(sname
));
1665 memcpy(ad
->ad_data
+ ADEDOFF_FINDERI_DOT_UND
,
1666 buf
+ AFP_OFF_FinderInfo
,
1669 ok
= set_delete_on_close(fsp
,
1671 fsp
->conn
->session_info
->security_token
,
1672 fsp
->conn
->session_info
->unix_token
);
1674 DBG_ERR("Deleting [%s] failed\n",
1675 smb_fname_str_dbg(sname
));
1683 if (is_afpresource_stream(stream
->name
)) {
1684 ad
->ad_rsrc_data
= talloc_size(ad
, stream
->size
);
1685 if (ad
->ad_rsrc_data
== NULL
) {
1690 nread
= SMB_VFS_PREAD(fsp
,
1694 if (nread
!= stream
->size
) {
1695 DBG_ERR("Bad size [%zd] on [%s]\n",
1696 (ssize_t
)stream
->size
,
1697 smb_fname_str_dbg(sname
));
1702 ad_setentrylen(ad
, ADEID_RFORK
, stream
->size
);
1704 if (!state
->have_adfile
) {
1706 * We have a resource *stream* but no AppleDouble
1707 * sidecar file, this means the share is configured with
1708 * fruit:resource=stream. So we should delete the
1711 ok
= set_delete_on_close(
1714 fsp
->conn
->session_info
->security_token
,
1715 fsp
->conn
->session_info
->unix_token
);
1717 DBG_ERR("Deleting [%s] failed\n",
1718 smb_fname_str_dbg(sname
));
1727 ad
->adx_entries
= talloc_realloc(ad
,
1729 struct ad_xattr_entry
,
1730 ad
->adx_header
.adx_num_attrs
+ 1);
1731 if (ad
->adx_entries
== NULL
) {
1736 e
= &ad
->adx_entries
[ad
->adx_header
.adx_num_attrs
];
1737 *e
= (struct ad_xattr_entry
) {
1738 .adx_length
= stream
->size
,
1740 e
->adx_name
= talloc_strdup(ad
, stream
->name
+ 1);
1741 if (e
->adx_name
== NULL
) {
1745 p
= strchr(e
->adx_name
, ':');
1750 status
= string_replace_allocate(handle
->conn
,
1755 vfs_translate_to_unix
);
1756 if (!NT_STATUS_IS_OK(status
) &&
1757 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1759 DBG_ERR("string_replace_allocate failed\n");
1764 e
->adx_name
= mapped_name
;
1765 e
->adx_namelen
= strlen(e
->adx_name
) + 1,
1767 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1768 ad
->adx_header
.adx_num_attrs
,
1770 (size_t)e
->adx_length
);
1772 ad
->adx_header
.adx_num_attrs
++;
1774 needed_size
= state
->adx_data_off
+ stream
->size
;
1775 if (needed_size
> talloc_get_size(ad
->adx_data
)) {
1776 ad
->adx_data
= talloc_realloc(ad
,
1780 if (ad
->adx_data
== NULL
) {
1786 nread
= SMB_VFS_PREAD(fsp
,
1787 ad
->adx_data
+ state
->adx_data_off
,
1790 if (nread
!= stream
->size
) {
1791 DBG_ERR("Bad size [%zd] on [%s]\n",
1792 (ssize_t
)stream
->size
,
1793 smb_fname_str_dbg(sname
));
1797 state
->adx_data_off
+= nread
;
1799 ok
= set_delete_on_close(fsp
,
1801 fsp
->conn
->session_info
->security_token
,
1802 fsp
->conn
->session_info
->unix_token
);
1804 DBG_ERR("Deleting [%s] failed\n",
1805 smb_fname_str_dbg(sname
));
1813 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1814 if (!NT_STATUS_IS_OK(status
)) {
1815 DBG_ERR("close_file [%s] failed: %s\n",
1816 smb_fname_str_dbg(smb_fname
),
1826 * Convert filesystem metadata to AppleDouble file
1828 bool ad_unconvert(TALLOC_CTX
*mem_ctx
,
1829 struct vfs_handle_struct
*handle
,
1830 const char *catia_mappings
,
1831 struct smb_filename
*smb_fname
,
1834 static struct char_mappings
**cmaps
= NULL
;
1835 TALLOC_CTX
*frame
= talloc_stackframe();
1836 struct ad_collect_state state
;
1837 struct stream_struct
*streams
= NULL
;
1838 struct smb_filename
*adpath
= NULL
;
1839 struct adouble
*ad
= NULL
;
1840 unsigned int num_streams
= 0;
1841 size_t to_convert
= 0;
1843 files_struct
*fsp
= NULL
;
1851 if (cmaps
== NULL
) {
1852 const char **mappings
= NULL
;
1854 mappings
= str_list_make_v3_const(
1855 frame
, catia_mappings
, NULL
);
1856 if (mappings
== NULL
) {
1860 cmaps
= string_replace_init_map(mem_ctx
, mappings
);
1861 TALLOC_FREE(mappings
);
1864 ok
= ad_unconvert_get_streams(handle
,
1873 for (i
= 0; i
< num_streams
; i
++) {
1874 if (strcasecmp_m(streams
[i
].name
, "::$DATA") == 0) {
1878 if (is_afpresource_stream(streams
[i
].name
)) {
1883 if (to_convert
== 0) {
1888 state
= (struct ad_collect_state
) {
1892 ret
= adouble_path(frame
, smb_fname
, &adpath
);
1898 ret
= SMB_VFS_STAT(handle
->conn
, adpath
);
1900 state
.have_adfile
= true;
1902 if (errno
!= ENOENT
) {
1906 state
.have_adfile
= false;
1909 if (to_convert
== 1 && have_rsrc
&& state
.have_adfile
) {
1911 * So we have just a single stream, the resource fork stream
1912 * from an AppleDouble file. Fine, that means there's nothing to
1919 ad
= ad_init(frame
, ADOUBLE_RSRC
);
1925 for (i
= 0; i
< num_streams
; i
++) {
1926 ok
= ad_collect_one_stream(handle
,
1937 ok
= ad_unconvert_open_ad(frame
, handle
, smb_fname
, adpath
, &fsp
);
1939 DBG_ERR("Failed to open adfile [%s]\n",
1940 smb_fname_str_dbg(smb_fname
));
1944 ret
= ad_fset(handle
, ad
, fsp
);
1955 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1956 if (!NT_STATUS_IS_OK(status
)) {
1957 DBG_ERR("close_file [%s] failed: %s\n",
1958 smb_fname_str_dbg(smb_fname
),
1968 * Read and parse Netatalk AppleDouble metadata xattr
1970 static ssize_t
ad_read_meta(vfs_handle_struct
*handle
,
1972 const struct smb_filename
*smb_fname
)
1978 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
1980 ealen
= SMB_VFS_GETXATTR(handle
->conn
, smb_fname
,
1981 AFPINFO_EA_NETATALK
, ad
->ad_data
,
1987 if (errno
== ENOATTR
) {
1993 DEBUG(2, ("error reading meta xattr: %s\n",
1999 if (ealen
!= AD_DATASZ_XATTR
) {
2000 DEBUG(2, ("bad size %zd\n", ealen
));
2006 /* Now parse entries */
2007 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
2009 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2015 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
2016 || !ad_getentryoff(ad
, ADEID_COMMENT
)
2017 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
2018 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
2019 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
2020 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
2021 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
2022 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
2023 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2030 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2031 smb_fname
->base_name
, rc
));
2035 if (errno
== EINVAL
) {
2037 (void)SMB_VFS_REMOVEXATTR(handle
->conn
,
2039 AFPINFO_EA_NETATALK
);
2047 static int ad_open_rsrc(vfs_handle_struct
*handle
,
2048 const struct smb_filename
*smb_fname
,
2051 files_struct
**_fsp
)
2054 struct smb_filename
*adp_smb_fname
= NULL
;
2055 files_struct
*fsp
= NULL
;
2056 uint32_t access_mask
;
2057 uint32_t share_access
;
2058 uint32_t create_disposition
;
2061 ret
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
2066 ret
= SMB_VFS_STAT(handle
->conn
, adp_smb_fname
);
2068 TALLOC_FREE(adp_smb_fname
);
2072 access_mask
= FILE_GENERIC_READ
;
2073 share_access
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
2074 create_disposition
= FILE_OPEN
;
2076 if (flags
& O_RDWR
) {
2077 access_mask
|= FILE_GENERIC_WRITE
;
2078 share_access
&= ~FILE_SHARE_WRITE
;
2081 status
= SMB_VFS_CREATE_FILE(
2082 handle
->conn
, /* conn */
2084 0, /* root_dir_fid */
2089 0, /* create_options */
2090 0, /* file_attributes */
2091 INTERNAL_OPEN_ONLY
, /* oplock_request */
2093 0, /* allocation_size */
2094 0, /* private_flags */
2099 NULL
, NULL
); /* create context */
2100 TALLOC_FREE(adp_smb_fname
);
2101 if (!NT_STATUS_IS_OK(status
)) {
2102 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2111 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2112 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2113 * for file IO on the ._ file.
2115 static int ad_open(vfs_handle_struct
*handle
,
2118 const struct smb_filename
*smb_fname
,
2124 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
2125 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2127 if (ad
->ad_type
== ADOUBLE_META
) {
2133 ad
->ad_opened
= false;
2137 ret
= ad_open_rsrc(handle
, smb_fname
, flags
, mode
, &ad
->ad_fsp
);
2141 ad
->ad_opened
= true;
2143 DBG_DEBUG("Path [%s] type [%s]\n",
2144 smb_fname
->base_name
,
2145 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
2150 static ssize_t
ad_read_rsrc_adouble(vfs_handle_struct
*handle
,
2152 const struct smb_filename
*smb_fname
)
2159 ret
= SMB_VFS_NEXT_FSTAT(handle
, ad
->ad_fsp
, &ad
->ad_fsp
->fsp_name
->st
);
2161 DBG_ERR("fstat [%s] failed: %s\n",
2162 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
2166 to_read
= ad
->ad_fsp
->fsp_name
->st
.st_ex_size
;
2167 if (to_read
> AD_XATTR_MAX_HDR_SIZE
) {
2168 to_read
= AD_XATTR_MAX_HDR_SIZE
;
2171 len
= SMB_VFS_NEXT_PREAD(handle
,
2176 if (len
!= to_read
) {
2177 DBG_NOTICE("%s %s: bad size: %zd\n",
2178 smb_fname
->base_name
, strerror(errno
), len
);
2182 /* Now parse entries */
2185 ad
->ad_fsp
->fsp_name
->st
.st_ex_size
);
2187 DBG_ERR("invalid AppleDouble resource %s\n",
2188 smb_fname
->base_name
);
2193 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
2194 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
2195 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
))
2197 DBG_ERR("invalid AppleDouble resource %s\n",
2198 smb_fname
->base_name
);
2207 * Read and parse resource fork, either ._ AppleDouble file or xattr
2209 static ssize_t
ad_read_rsrc(vfs_handle_struct
*handle
,
2211 const struct smb_filename
*smb_fname
)
2213 return ad_read_rsrc_adouble(handle
, ad
, smb_fname
);
2217 * Read and unpack an AppleDouble metadata xattr or resource
2219 static ssize_t
ad_read(vfs_handle_struct
*handle
,
2221 const struct smb_filename
*smb_fname
)
2223 switch (ad
->ad_type
) {
2225 return ad_read_meta(handle
, ad
, smb_fname
);
2227 return ad_read_rsrc(handle
, ad
, smb_fname
);
2233 static int adouble_destructor(struct adouble
*ad
)
2237 if (!ad
->ad_opened
) {
2241 SMB_ASSERT(ad
->ad_fsp
!= NULL
);
2243 status
= close_file(NULL
, ad
->ad_fsp
, NORMAL_CLOSE
);
2244 if (!NT_STATUS_IS_OK(status
)) {
2245 DBG_ERR("Closing [%s] failed: %s\n",
2246 fsp_str_dbg(ad
->ad_fsp
), nt_errstr(status
));
2253 * Allocate a struct adouble without initialiing it
2255 * The struct is either hang of the fsp extension context or if fsp is
2258 * @param[in] ctx talloc context
2259 * @param[in] handle vfs handle
2260 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2262 * @return adouble handle
2264 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
,
2265 adouble_type_t type
)
2273 adsize
= AD_DATASZ_XATTR
;
2277 * AppleDouble ._ file case, optimize for fewer (but larger)
2280 * - without xattrs size of the header is exactly
2281 * AD_DATASZ_DOT_UND (82) bytes
2283 * - with embedded xattrs it can be larger, up to
2284 * AD_XATTR_MAX_HDR_SIZE
2286 * Larger headers are not supported, but this is a reasonable
2287 * limit that is also employed by the macOS client.
2289 * We used the largest possible size to be able to read the full
2290 * header with one IO.
2292 adsize
= AD_XATTR_MAX_HDR_SIZE
;
2298 ad
= talloc_zero(ctx
, struct adouble
);
2305 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
2306 if (ad
->ad_data
== NULL
) {
2313 ad
->ad_magic
= AD_MAGIC
;
2314 ad
->ad_version
= AD_VERSION
;
2316 talloc_set_destructor(ad
, adouble_destructor
);
2326 * Allocate and initialize a new struct adouble
2328 * @param[in] ctx talloc context
2329 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2331 * @return adouble handle, initialized
2333 struct adouble
*ad_init(TALLOC_CTX
*ctx
, adouble_type_t type
)
2336 const struct ad_entry_order
*eid
;
2337 struct adouble
*ad
= NULL
;
2338 time_t t
= time(NULL
);
2342 eid
= entry_order_meta_xattr
;
2345 eid
= entry_order_dot_und
;
2351 ad
= ad_alloc(ctx
, type
);
2357 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
2358 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
2362 /* put something sane in the date fields */
2363 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
2364 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
2365 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
2366 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
2374 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
2375 vfs_handle_struct
*handle
,
2377 const struct smb_filename
*smb_fname
,
2378 adouble_type_t type
)
2382 struct adouble
*ad
= NULL
;
2386 smb_fname
= fsp
->base_fsp
->fsp_name
;
2389 DEBUG(10, ("ad_get(%s) called for %s\n",
2390 type
== ADOUBLE_META
? "meta" : "rsrc",
2391 smb_fname
->base_name
));
2393 ad
= ad_alloc(ctx
, type
);
2399 /* Try rw first so we can use the fd in ad_convert() */
2402 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2403 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
2405 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
2408 DBG_DEBUG("ad_open [%s] error [%s]\n",
2409 smb_fname
->base_name
, strerror(errno
));
2414 len
= ad_read(handle
, ad
, smb_fname
);
2416 DEBUG(10, ("error reading AppleDouble for %s\n",
2417 smb_fname
->base_name
));
2423 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2424 type
== ADOUBLE_META
? "meta" : "rsrc",
2425 smb_fname
->base_name
, rc
));
2434 * Return AppleDouble data for a file
2436 * @param[in] ctx talloc context
2437 * @param[in] handle vfs handle
2438 * @param[in] smb_fname pathname to file or directory
2439 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2441 * @return talloced struct adouble or NULL on error
2443 struct adouble
*ad_get(TALLOC_CTX
*ctx
,
2444 vfs_handle_struct
*handle
,
2445 const struct smb_filename
*smb_fname
,
2446 adouble_type_t type
)
2448 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
2452 * Return AppleDouble data for a file
2454 * @param[in] ctx talloc context
2455 * @param[in] handle vfs handle
2456 * @param[in] fsp fsp to use for IO
2457 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2459 * @return talloced struct adouble or NULL on error
2461 struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
2462 files_struct
*fsp
, adouble_type_t type
)
2464 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
2468 * Set AppleDouble metadata on a file or directory
2470 * @param[in] ad adouble handle
2472 * @param[in] smb_fname pathname to file or directory
2474 * @return status code, 0 means success
2476 int ad_set(vfs_handle_struct
*handle
,
2478 const struct smb_filename
*smb_fname
)
2483 DBG_DEBUG("Path [%s]\n", smb_fname
->base_name
);
2485 if (ad
->ad_type
!= ADOUBLE_META
) {
2486 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2487 smb_fname
->base_name
);
2491 ok
= ad_pack(handle
, ad
, NULL
);
2496 ret
= SMB_VFS_SETXATTR(handle
->conn
,
2498 AFPINFO_EA_NETATALK
,
2500 AD_DATASZ_XATTR
, 0);
2502 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname
->base_name
, ret
);
2508 * Set AppleDouble metadata on a file or directory
2510 * @param[in] ad adouble handle
2511 * @param[in] fsp file handle
2513 * @return status code, 0 means success
2515 int ad_fset(struct vfs_handle_struct
*handle
,
2523 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2526 || (fsp
->fh
== NULL
)
2527 || (fsp
->fh
->fd
== -1))
2529 smb_panic("bad fsp");
2532 ok
= ad_pack(handle
, ad
, fsp
);
2537 switch (ad
->ad_type
) {
2539 rc
= SMB_VFS_NEXT_SETXATTR(handle
,
2541 AFPINFO_EA_NETATALK
,
2543 AD_DATASZ_XATTR
, 0);
2547 len
= SMB_VFS_NEXT_PWRITE(handle
,
2550 ad_getentryoff(ad
, ADEID_RFORK
),
2552 if (len
!= ad_getentryoff(ad
, ADEID_RFORK
)) {
2553 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp
), len
);
2563 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2568 bool is_adouble_file(const char *path
)
2570 const char *p
= NULL
;
2573 p
= strrchr(path
, '/');
2581 ADOUBLE_NAME_PREFIX
,
2582 strlen(ADOUBLE_NAME_PREFIX
));
2590 * Prepend "._" to a basename
2591 * Return a new struct smb_filename with stream_name == NULL.
2593 int adouble_path(TALLOC_CTX
*ctx
,
2594 const struct smb_filename
*smb_fname_in
,
2595 struct smb_filename
**pp_smb_fname_out
)
2599 struct smb_filename
*smb_fname
= cp_smb_filename(ctx
,
2602 if (smb_fname
== NULL
) {
2606 /* We need streamname to be NULL */
2607 TALLOC_FREE(smb_fname
->stream_name
);
2609 /* And we're replacing base_name. */
2610 TALLOC_FREE(smb_fname
->base_name
);
2612 SET_STAT_INVALID(smb_fname
->st
);
2614 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2616 TALLOC_FREE(smb_fname
);
2620 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2621 "%s/._%s", parent
, base
);
2622 if (smb_fname
->base_name
== NULL
) {
2623 TALLOC_FREE(smb_fname
);
2627 *pp_smb_fname_out
= smb_fname
;
2633 * Allocate and initialize an AfpInfo struct
2635 AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2637 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2641 ai
->afpi_Signature
= AFP_Signature
;
2642 ai
->afpi_Version
= AFP_Version
;
2643 ai
->afpi_BackupTime
= AD_DATE_START
;
2648 * Pack an AfpInfo struct into a buffer
2650 * Buffer size must be at least AFP_INFO_SIZE
2651 * Returns size of packed buffer
2653 ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2655 memset(buf
, 0, AFP_INFO_SIZE
);
2657 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2658 RSIVAL(buf
, 4, ai
->afpi_Version
);
2659 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2660 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2662 return AFP_INFO_SIZE
;
2666 * Unpack a buffer into a AfpInfo structure
2668 * Buffer size must be at least AFP_INFO_SIZE
2669 * Returns allocated AfpInfo struct
2671 AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
)
2673 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2678 ai
->afpi_Signature
= RIVAL(data
, 0);
2679 ai
->afpi_Version
= RIVAL(data
, 4);
2680 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2681 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2682 sizeof(ai
->afpi_FinderInfo
));
2684 if (ai
->afpi_Signature
!= AFP_Signature
2685 || ai
->afpi_Version
!= AFP_Version
) {
2686 DEBUG(1, ("Bad AfpInfo signature or version\n"));