42b2e808d66743a92b7c0028b88e65e4d09012a4
[bbaumbach/samba-autobuild/.git] / source3 / lib / adouble.c
1 /*
2  * Samba AppleDouble helpers
3  *
4  * Copyright (C) Ralph Boehme, 2019
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include "includes.h"
21 #include "adouble.h"
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"
28 #include "auth.h"
29
30 /*
31    "._" AppleDouble Header File Layout:
32
33          MAGIC          0x00051607
34          VERSION        0x00020000
35          FILLER         0
36          COUNT          2
37      .-- AD ENTRY[0]    Finder Info Entry (must be first)
38   .--+-- AD ENTRY[1]    Resource Fork Entry (must be last)
39   |  |   /////////////
40   |  '-> FINDER INFO    Fixed Size Data (32 Bytes)
41   |      ~~~~~~~~~~~~~  2 Bytes Padding
42   |      EXT ATTR HDR   Fixed Size Data (36 Bytes)
43   |      /////////////
44   |      ATTR ENTRY[0] --.
45   |      ATTR ENTRY[1] --+--.
46   |      ATTR ENTRY[2] --+--+--.
47   |         ...          |  |  |
48   |      ATTR ENTRY[N] --+--+--+--.
49   |      ATTR DATA 0   <-'  |  |  |
50   |      ////////////       |  |  |
51   |      ATTR DATA 1   <----'  |  |
52   |      /////////////         |  |
53   |      ATTR DATA 2   <-------'  |
54   |      /////////////            |
55   |         ...                   |
56   |      ATTR DATA N   <----------'
57   |      /////////////
58   |         ...          Attribute Free Space
59   |
60   '----> RESOURCE FORK
61             ...          Variable Sized Data
62             ...
63 */
64
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
69
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)
83
84 /* Offsets */
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)
89
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 + \
95                                   ADEDLEN_FILEDATESI)
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)
100
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)
104
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)
111
112 #if AD_DATASZ_XATTR != 402
113 #error bad size for AD_DATASZ_XATTR
114 #endif
115
116 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
117                            (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
118                            ADEDLEN_FINDERI)
119 #if AD_DATASZ_DOT_UND != 82
120 #error bad size for AD_DATASZ_DOT_UND
121 #endif
122
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)
128
129 /*
130  * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131  * representation as well as the on-disk format.
132  *
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.
136  *
137  * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
138  */
139
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];
147         uint16_t adx_flags;
148         uint16_t adx_num_attrs;
149 };
150
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 */
155         uint16_t adx_flags;
156         uint8_t  adx_namelen;   /* included the NULL terminator */
157         char    *adx_name;      /* NULL-terminated UTF-8 name */
158 };
159
160 struct ad_entry {
161         size_t ade_off;
162         size_t ade_len;
163 };
164
165 struct adouble {
166         files_struct             *ad_fsp;
167         bool                      ad_opened;
168         adouble_type_t            ad_type;
169         uint32_t                  ad_magic;
170         uint32_t                  ad_version;
171         uint8_t                   ad_filler[ADEDLEN_FILLER];
172         struct ad_entry           ad_eid[ADEID_MAX];
173         char                     *ad_data;
174         char                     *ad_rsrc_data;
175         struct ad_xattr_header    adx_header;
176         struct ad_xattr_entry    *adx_entries;
177         char                     *adx_data;
178 };
179
180 struct ad_entry_order {
181         uint32_t id, offset, len;
182 };
183
184 /* Netatalk AppleDouble metadata xattr */
185 static const
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},
195         {0, 0, 0}
196 };
197
198 /* AppleDouble resource fork file (the ones prefixed by "._") */
199 static const
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},
203         {0, 0, 0}
204 };
205
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
211 };
212
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
250 };
251
252 size_t ad_getentrylen(const struct adouble *ad, int eid)
253 {
254         return ad->ad_eid[eid].ade_len;
255 }
256
257 size_t ad_getentryoff(const struct adouble *ad, int eid)
258 {
259         return ad->ad_eid[eid].ade_off;
260 }
261
262 size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
263 {
264         return ad->ad_eid[eid].ade_len = len;
265 }
266
267 size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
268 {
269         return ad->ad_eid[eid].ade_off = off;
270 }
271
272 /**
273  * Return a pointer to an AppleDouble entry
274  *
275  * Returns NULL if the entry is not present
276  **/
277 char *ad_get_entry(const struct adouble *ad, int eid)
278 {
279         off_t off = ad_getentryoff(ad, eid);
280         size_t len = ad_getentrylen(ad, eid);
281
282         if (off == 0 || len == 0) {
283                 return NULL;
284         }
285
286         return ad->ad_data + off;
287 }
288
289 /**
290  * Get a date
291  **/
292 int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
293 {
294         bool xlate = (dateoff & AD_DATE_UNIX);
295         char *p = NULL;
296
297         dateoff &= AD_DATE_MASK;
298         p = ad_get_entry(ad, ADEID_FILEDATESI);
299         if (p == NULL) {
300                 return -1;
301         }
302
303         if (dateoff > AD_DATE_ACCESS) {
304             return -1;
305         }
306
307         memcpy(date, p + dateoff, sizeof(uint32_t));
308
309         if (xlate) {
310                 *date = AD_DATE_TO_UNIX(*date);
311         }
312         return 0;
313 }
314
315 /**
316  * Set a date
317  **/
318 int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
319 {
320         bool xlate = (dateoff & AD_DATE_UNIX);
321         char *p = NULL;
322
323         p = ad_get_entry(ad, ADEID_FILEDATESI);
324         if (p == NULL) {
325                 return -1;
326         }
327
328         dateoff &= AD_DATE_MASK;
329         if (xlate) {
330                 date = AD_DATE_FROM_UNIX(date);
331         }
332
333         if (dateoff > AD_DATE_ACCESS) {
334                 return -1;
335         }
336
337         memcpy(p + dateoff, &date, sizeof(date));
338
339         return 0;
340 }
341
342
343 /**
344  * Map on-disk AppleDouble id to enumerated id
345  **/
346 static uint32_t get_eid(uint32_t eid)
347 {
348         if (eid <= 15) {
349                 return eid;
350         }
351
352         switch (eid) {
353         case AD_DEV:
354                 return ADEID_PRIVDEV;
355         case AD_INO:
356                 return ADEID_PRIVINO;
357         case AD_SYN:
358                 return ADEID_PRIVSYN;
359         case AD_ID:
360                 return ADEID_PRIVID;
361         default:
362                 break;
363         }
364
365         return 0;
366 }
367
368 /*
369  * Move resourcefork data in an AppleDouble file
370  *
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.
374  *
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.
378  */
379 static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
380                               struct adouble *ad,
381                               files_struct *fsp)
382 {
383         size_t reso_len;
384         size_t reso_off;
385         size_t n;
386         bool ok;
387
388         reso_len = ad_getentrylen(ad, ADEID_RFORK);
389         reso_off = ad_getentryoff(ad, ADEID_RFORK);
390
391         if (reso_len == 0) {
392                 return true;
393         }
394
395         if (ad->ad_rsrc_data == NULL) {
396                 /*
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.
401                  */
402                 ad->ad_rsrc_data = talloc_size(ad, reso_len);
403                 if (ad->ad_rsrc_data == NULL) {
404                         return false;
405                 }
406
407                 n = SMB_VFS_NEXT_PREAD(handle,
408                                        fsp,
409                                        ad->ad_rsrc_data,
410                                        reso_len,
411                                        ADEDOFF_RFORK_DOT_UND);
412                 if (n != reso_len) {
413                         DBG_ERR("Read on [%s] failed\n",
414                                 fsp_str_dbg(fsp));
415                         ok = false;
416                         goto out;
417                 }
418         }
419
420         n = SMB_VFS_NEXT_PWRITE(handle,
421                                 fsp,
422                                 ad->ad_rsrc_data,
423                                 reso_len,
424                                 reso_off);
425         if (n != reso_len) {
426                 DBG_ERR("Write on [%s] failed\n",
427                         fsp_str_dbg(fsp));
428                 ok = false;
429                 goto out;
430         }
431
432         ok = true;
433 out:
434         return ok;
435 }
436
437 static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
438                            struct adouble *ad,
439                            files_struct *fsp)
440 {
441         struct ad_xattr_header *h = &ad->adx_header;
442         size_t oldsize;
443         uint32_t off;
444         uint32_t data_off;
445         uint16_t i;
446         bool ok;
447
448         if (ad->adx_entries == NULL) {
449                 /* No xattrs, nothing to pack */
450                 return true;
451         }
452
453         if (fsp == NULL) {
454                 DBG_ERR("fsp unexpectedly NULL\n");
455                 return false;
456         }
457
458         oldsize = talloc_get_size(ad->ad_data);
459         if (oldsize < AD_XATTR_MAX_HDR_SIZE) {
460                 ad->ad_data = talloc_realloc(ad,
461                                              ad->ad_data,
462                                              char,
463                                              AD_XATTR_MAX_HDR_SIZE);
464                 if (ad->ad_data == NULL) {
465                         return false;
466                 }
467                 memset(ad->ad_data + oldsize,
468                        0,
469                        AD_XATTR_MAX_HDR_SIZE - oldsize);
470         }
471
472         /*
473          * First, let's calculate the start of the xattr data area which will be
474          * after the xattr header + header entries.
475          */
476
477         data_off = ad_getentryoff(ad, ADEID_FINDERI);
478         data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
479         /* 2 bytes padding */
480         data_off += 2;
481
482         for (i = 0; i < h->adx_num_attrs; i++) {
483                 struct ad_xattr_entry *e = &ad->adx_entries[i];
484
485                 /* Align on 4 byte boundary */
486                 data_off = (data_off + 3) & ~3;
487
488                 data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
489                 if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
490                         return false;
491                 }
492         }
493
494         off = ad_getentryoff(ad, ADEID_FINDERI);
495         off +=  ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
496         /* 2 bytes padding */
497         off += 2;
498
499         for (i = 0; i < h->adx_num_attrs; i++) {
500                 struct ad_xattr_entry *e = &ad->adx_entries[i];
501
502                 /* Align on 4 byte boundary */
503                 off = (off + 3) & ~3;
504
505                 e->adx_offset = data_off;
506                 data_off += e->adx_length;
507
508                 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
509                           "adx_data_off [%zu]\n",
510                           (size_t)i,
511                           e->adx_name,
512                           (size_t)e->adx_namelen,
513                           (size_t)off,
514                           (size_t)e->adx_length,
515                           (size_t)e->adx_offset);
516
517                 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
518                         return false;
519                 }
520                 RSIVAL(ad->ad_data, off, e->adx_offset);
521                 off += 4;
522
523                 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
524                         return false;
525                 }
526                 RSIVAL(ad->ad_data, off, e->adx_length);
527                 off += 4;
528
529                 if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
530                         return false;
531                 }
532                 RSSVAL(ad->ad_data, off, e->adx_flags);
533                 off += 2;
534
535                 if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
536                         return false;
537                 }
538                 SCVAL(ad->ad_data, off, e->adx_namelen);
539                 off += 1;
540
541                 if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
542                         return false;
543                 }
544                 memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
545                 off += e->adx_namelen;
546         }
547
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;
551
552         if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
553                 ad->ad_data = talloc_realloc(ad,
554                                              ad->ad_data,
555                                              char,
556                                              h->adx_total_size);
557                 if (ad->ad_data == NULL) {
558                         return false;
559                 }
560         }
561
562         memcpy(ad->ad_data + h->adx_data_start,
563                ad->adx_data,
564                h->adx_data_length);
565
566         ad_setentrylen(ad,
567                        ADEID_FINDERI,
568                        h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
569
570         ad_setentryoff(ad,
571                        ADEID_RFORK,
572                        ad_getentryoff(ad, ADEID_FINDERI) +
573                        ad_getentrylen(ad, ADEID_FINDERI));
574
575         memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
576
577         /*
578          * Rewind, then update the header fields.
579          */
580
581         off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
582         /* 2 bytes padding */
583         off += 2;
584
585         RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
586         off += 4;
587         RSIVAL(ad->ad_data, off, 0);
588         off += 4;
589         RSIVAL(ad->ad_data, off, h->adx_total_size);
590         off += 4;
591         RSIVAL(ad->ad_data, off, h->adx_data_start);
592         off += 4;
593         RSIVAL(ad->ad_data, off, h->adx_data_length);
594         off += 4;
595
596         /* adx_reserved and adx_flags */
597         memset(ad->ad_data + off, 0, 3 * 4 + 2);
598         off += 3 * 4 + 2;
599
600         RSSVAL(ad->ad_data, off, h->adx_num_attrs);
601         off += 2;
602
603         ok = ad_pack_move_reso(handle, ad, fsp);
604         if (!ok) {
605                 DBG_ERR("Moving resourcefork of [%s] failed\n",
606                         fsp_str_dbg(fsp));
607                 return false;
608         }
609
610         return true;
611 }
612
613 /**
614  * Pack AppleDouble structure into data buffer
615  **/
616 static bool ad_pack(struct vfs_handle_struct *handle,
617                     struct adouble *ad,
618                     files_struct *fsp)
619 {
620         uint32_t       eid;
621         uint16_t       nent;
622         uint32_t       bufsize;
623         uint32_t       offset = 0;
624         bool ok;
625
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);
629                 return false;
630         }
631
632         if (offset + ADEDLEN_MAGIC < offset ||
633                         offset + ADEDLEN_MAGIC >= bufsize) {
634                 return false;
635         }
636         RSIVAL(ad->ad_data, offset, ad->ad_magic);
637         offset += ADEDLEN_MAGIC;
638
639         if (offset + ADEDLEN_VERSION < offset ||
640                         offset + ADEDLEN_VERSION >= bufsize) {
641                 return false;
642         }
643         RSIVAL(ad->ad_data, offset, ad->ad_version);
644         offset += ADEDLEN_VERSION;
645
646         if (offset + ADEDLEN_FILLER < offset ||
647                         offset + ADEDLEN_FILLER >= bufsize) {
648                 return false;
649         }
650         if (ad->ad_type == ADOUBLE_RSRC) {
651                 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
652         }
653         offset += ADEDLEN_FILLER;
654
655         if (offset + ADEDLEN_NENTRIES < offset ||
656                         offset + ADEDLEN_NENTRIES >= bufsize) {
657                 return false;
658         }
659         offset += ADEDLEN_NENTRIES;
660
661         ok = ad_pack_xattrs(handle, ad, fsp);
662         if (!ok) {
663                 return false;
664         }
665
666         for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
667                 if (ad->ad_eid[eid].ade_off == 0) {
668                         /*
669                          * ade_off is also used as indicator whether a
670                          * specific entry is used or not
671                          */
672                         continue;
673                 }
674
675                 if (offset + AD_ENTRY_LEN_EID < offset ||
676                                 offset + AD_ENTRY_LEN_EID >= bufsize) {
677                         return false;
678                 }
679                 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
680                 offset += AD_ENTRY_LEN_EID;
681
682                 if (offset + AD_ENTRY_LEN_OFF < offset ||
683                                 offset + AD_ENTRY_LEN_OFF >= bufsize) {
684                         return false;
685                 }
686                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
687                 offset += AD_ENTRY_LEN_OFF;
688
689                 if (offset + AD_ENTRY_LEN_LEN < offset ||
690                                 offset + AD_ENTRY_LEN_LEN >= bufsize) {
691                         return false;
692                 }
693                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
694                 offset += AD_ENTRY_LEN_LEN;
695
696                 nent++;
697         }
698
699         if (ADEDOFF_NENTRIES + 2 >= bufsize) {
700                 return false;
701         }
702         RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
703
704         return true;
705 }
706
707 static bool ad_unpack_xattrs(struct adouble *ad)
708 {
709         struct ad_xattr_header *h = &ad->adx_header;
710         const char *p = ad->ad_data;
711         uint32_t hoff;
712         uint32_t i;
713
714         if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
715                 return true;
716         }
717
718         /* 2 bytes padding */
719         hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
720
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);
728
729         if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
730                 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
731                 return false;
732         }
733
734         if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
735                 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
736                 return false;
737         }
738         if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
739                 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
740                 return false;
741         }
742
743         if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
744                 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
745                 return false;
746         }
747
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);
750                 return false;
751         }
752         if ((h->adx_data_start + h->adx_data_length) >
753             ad->adx_header.adx_total_size)
754         {
755                 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
756                 return false;
757         }
758
759         if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
760                 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
761                 return false;
762         }
763
764         if (h->adx_num_attrs == 0) {
765                 return true;
766         }
767
768         ad->adx_entries = talloc_zero_array(
769                 ad, struct ad_xattr_entry, h->adx_num_attrs);
770         if (ad->adx_entries == NULL) {
771                 return false;
772         }
773
774         hoff += AD_XATTR_HDR_SIZE;
775
776         for (i = 0; i < h->adx_num_attrs; i++) {
777                 struct ad_xattr_entry *e = &ad->adx_entries[i];
778
779                 hoff = (hoff + 3) & ~3;
780
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);
785
786                 if (e->adx_offset >= ad->adx_header.adx_total_size) {
787                         DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
788                                 e->adx_offset);
789                         return false;
790                 }
791
792                 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
793                         DBG_ERR("Bad adx_length: %" PRIx32 "\n",
794                                 e->adx_length);
795                         return false;
796                 }
797
798                 if ((e->adx_offset + e->adx_length) >
799                     ad->adx_header.adx_total_size)
800                 {
801                         DBG_ERR("Bad adx_length: %" PRIx32 "\n",
802                                 e->adx_length);
803                         return false;
804                 }
805
806                 if (e->adx_namelen == 0) {
807                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
808                                 e->adx_namelen);
809                         return false;
810                 }
811                 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
812                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
813                                 e->adx_namelen);
814                         return false;
815                 }
816                 if ((hoff + 11 + e->adx_namelen) >
817                     ad->adx_header.adx_data_start)
818                 {
819                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
820                                 e->adx_namelen);
821                         return false;
822                 }
823
824                 e->adx_name = talloc_strndup(ad->adx_entries,
825                                              p + hoff + 11,
826                                              e->adx_namelen);
827                 if (e->adx_name == NULL) {
828                         return false;
829                 }
830
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),
834                           e->adx_length);
835
836                 hoff += 11 + e->adx_namelen;
837         }
838
839         return true;
840 }
841
842 /**
843  * Unpack an AppleDouble blob into a struct adoble
844  **/
845 static bool ad_unpack(struct adouble *ad, const size_t nentries,
846                       size_t filesize)
847 {
848         size_t bufsize = talloc_get_size(ad->ad_data);
849         size_t adentries, i;
850         uint32_t eid, len, off;
851         bool ok;
852
853         /*
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.
858          */
859
860         if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
861                 DEBUG(1, ("bad size\n"));
862                 return false;
863         }
864
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"));
869                 return false;
870         }
871
872         memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
873
874         adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
875         if (adentries != nentries) {
876                 DEBUG(1, ("invalid number of entries: %zu\n",
877                           adentries));
878                 return false;
879         }
880
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));
884                 eid = get_eid(eid);
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);
887
888                 if (!eid || eid >= ADEID_MAX) {
889                         DEBUG(1, ("bogus eid %d\n", eid));
890                         return false;
891                 }
892
893                 /*
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
897                  */
898                 if ((off > bufsize) && (eid != ADEID_RFORK)) {
899                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
900                                   eid, off, len));
901                         return false;
902                 }
903
904                 /*
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.
912                  */
913                 if ((eid != ADEID_RFORK) &&
914                     (eid != ADEID_FINDERI) &&
915                     ((off + len) > bufsize)) {
916                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
917                                   eid, off, len));
918                         return false;
919                 }
920
921                 /*
922                  * That would be obviously broken
923                  */
924                 if (off > filesize) {
925                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
926                                   eid, off, len));
927                         return false;
928                 }
929
930                 /*
931                  * Check for any entry that has its end beyond the
932                  * filesize.
933                  */
934                 if (off + len < off) {
935                         DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
936                                   ", len: %" PRIu32 "\n",
937                                   eid, off, len));
938                         return false;
939
940                 }
941                 if (off + len > filesize) {
942                         /*
943                          * If this is the resource fork entry, we fix
944                          * up the length, for any other entry we bail
945                          * out.
946                          */
947                         if (eid != ADEID_RFORK) {
948                                 DEBUG(1, ("bogus eid %d: off: %" PRIu32
949                                           ", len: %" PRIu32 "\n",
950                                           eid, off, len));
951                                 return false;
952                         }
953
954                         /*
955                          * Fixup the resource fork entry by limiting
956                          * the size to entryoffset - filesize.
957                          */
958                         len = filesize - off;
959                         DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
960                                   ", len: %" PRIu32 "\n", off, len));
961                 }
962
963                 ad->ad_eid[eid].ade_off = off;
964                 ad->ad_eid[eid].ade_len = len;
965         }
966
967         ok = ad_unpack_xattrs(ad);
968         if (!ok) {
969                 return false;
970         }
971
972         return true;
973 }
974
975 static bool ad_convert_move_reso(vfs_handle_struct *handle,
976                                  struct adouble *ad,
977                                  const struct smb_filename *smb_fname)
978 {
979         char *buf = NULL;
980         size_t rforklen;
981         size_t rforkoff;
982         ssize_t n;
983         int ret;
984
985         rforklen = ad_getentrylen(ad, ADEID_RFORK);
986         if (rforklen == 0) {
987                 return true;
988         }
989
990         buf = talloc_size(ad, rforklen);
991         if (buf == NULL) {
992                 /*
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.
997                  */
998                 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
999                         rforklen);
1000                 return false;
1001         }
1002
1003         rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1004
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));
1009                 return false;
1010         }
1011
1012         rforkoff = ADEDOFF_RFORK_DOT_UND;
1013
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));
1018                 return false;
1019         }
1020
1021         ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1022
1023         ret = ad_fset(handle, ad, ad->ad_fsp);
1024         if (ret != 0) {
1025                 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1026                 return false;
1027         }
1028
1029         return true;
1030 }
1031
1032 static bool ad_convert_xattr(vfs_handle_struct *handle,
1033                              struct adouble *ad,
1034                              const struct smb_filename *smb_fname,
1035                              const char *catia_mappings,
1036                              bool *converted_xattr)
1037 {
1038         static struct char_mappings **string_replace_cmaps = NULL;
1039         uint16_t i;
1040         int saved_errno = 0;
1041         NTSTATUS status;
1042         int rc;
1043         bool ok;
1044
1045         *converted_xattr = false;
1046
1047         if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1048                 return true;
1049         }
1050
1051         if (string_replace_cmaps == NULL) {
1052                 const char **mappings = NULL;
1053
1054                 mappings = str_list_make_v3_const(
1055                         talloc_tos(), catia_mappings, NULL);
1056                 if (mappings == NULL) {
1057                         return false;
1058                 }
1059                 string_replace_cmaps = string_replace_init_map(
1060                         handle->conn->sconn, mappings);
1061                 TALLOC_FREE(mappings);
1062         }
1063
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;
1067                 char *tmp = NULL;
1068                 struct smb_filename *stream_name = NULL;
1069                 files_struct *fsp = NULL;
1070                 ssize_t nwritten;
1071
1072                 status = string_replace_allocate(handle->conn,
1073                                                  e->adx_name,
1074                                                  string_replace_cmaps,
1075                                                  talloc_tos(),
1076                                                  &mapped_name,
1077                                                  vfs_translate_to_windows);
1078                 if (!NT_STATUS_IS_OK(status) &&
1079                     !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1080                 {
1081                         DBG_ERR("string_replace_allocate failed\n");
1082                         ok = false;
1083                         goto fail;
1084                 }
1085
1086                 tmp = mapped_name;
1087                 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1088                 TALLOC_FREE(tmp);
1089                 if (mapped_name == NULL) {
1090                         ok = false;
1091                         goto fail;
1092                 }
1093
1094                 stream_name = synthetic_smb_fname(talloc_tos(),
1095                                                   smb_fname->base_name,
1096                                                   mapped_name,
1097                                                   NULL,
1098                                                   smb_fname->twrp,
1099                                                   smb_fname->flags);
1100                 TALLOC_FREE(mapped_name);
1101                 if (stream_name == NULL) {
1102                         DBG_ERR("synthetic_smb_fname failed\n");
1103                         ok = false;
1104                         goto fail;
1105                 }
1106
1107                 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1108
1109                 status = SMB_VFS_CREATE_FILE(
1110                         handle->conn,                   /* conn */
1111                         NULL,                           /* req */
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 */
1120                         NULL,                           /* lease */
1121                         0,                              /* allocation_size */
1122                         0,                              /* private_flags */
1123                         NULL,                           /* sd */
1124                         NULL,                           /* ea_list */
1125                         &fsp,                           /* result */
1126                         NULL,                           /* psbuf */
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");
1131                         ok = false;
1132                         goto fail;
1133                 }
1134
1135                 nwritten = SMB_VFS_PWRITE(fsp,
1136                                           ad->ad_data + e->adx_offset,
1137                                           e->adx_length,
1138                                           0);
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;
1144                         ok = false;
1145                         goto fail;
1146                 }
1147
1148                 status = close_file(NULL, fsp, NORMAL_CLOSE);
1149                 if (!NT_STATUS_IS_OK(status)) {
1150                         ok = false;
1151                         goto fail;
1152                 }
1153                 fsp = NULL;
1154         }
1155
1156         ad->adx_header.adx_num_attrs = 0;
1157         TALLOC_FREE(ad->adx_entries);
1158
1159         ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1160
1161         rc = ad_fset(handle, ad, ad->ad_fsp);
1162         if (rc != 0) {
1163                 DBG_ERR("ad_fset on [%s] failed: %s\n",
1164                         fsp_str_dbg(ad->ad_fsp), strerror(errno));
1165                 ok = false;
1166                 goto fail;
1167         }
1168
1169         ok = ad_convert_move_reso(handle, ad, smb_fname);
1170         if (!ok) {
1171                 goto fail;
1172         }
1173
1174         *converted_xattr = true;
1175         ok = true;
1176
1177 fail:
1178         return ok;
1179 }
1180
1181 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1182                                   struct adouble *ad,
1183                                   const struct smb_filename *smb_fname)
1184 {
1185         char *p_ad = NULL;
1186         AfpInfo *ai = NULL;
1187         DATA_BLOB aiblob;
1188         struct smb_filename *stream_name = NULL;
1189         files_struct *fsp = NULL;
1190         size_t size;
1191         ssize_t nwritten;
1192         NTSTATUS status;
1193         int saved_errno = 0;
1194         int cmp;
1195
1196         cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1197         if (cmp != 0) {
1198                 return true;
1199         }
1200
1201         p_ad = ad_get_entry(ad, ADEID_FINDERI);
1202         if (p_ad == NULL) {
1203                 return false;
1204         }
1205
1206         ai = afpinfo_new(talloc_tos());
1207         if (ai == NULL) {
1208                 return false;
1209         }
1210
1211         memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1212
1213         aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1214         if (aiblob.data == NULL) {
1215                 TALLOC_FREE(ai);
1216                 return false;
1217         }
1218
1219         size = afpinfo_pack(ai, (char *)aiblob.data);
1220         TALLOC_FREE(ai);
1221         if (size != AFP_INFO_SIZE) {
1222                 return false;
1223         }
1224
1225         stream_name = synthetic_smb_fname(talloc_tos(),
1226                                           smb_fname->base_name,
1227                                           AFPINFO_STREAM,
1228                                           NULL,
1229                                           smb_fname->twrp,
1230                                           smb_fname->flags);
1231         if (stream_name == NULL) {
1232                 data_blob_free(&aiblob);
1233                 DBG_ERR("synthetic_smb_fname failed\n");
1234                 return false;
1235         }
1236
1237         DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1238
1239         status = SMB_VFS_CREATE_FILE(
1240                 handle->conn,                   /* conn */
1241                 NULL,                           /* req */
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 */
1250                 NULL,                           /* lease */
1251                 0,                              /* allocation_size */
1252                 0,                              /* private_flags */
1253                 NULL,                           /* sd */
1254                 NULL,                           /* ea_list */
1255                 &fsp,                           /* result */
1256                 NULL,                           /* psbuf */
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");
1261                 return false;
1262         }
1263
1264         nwritten = SMB_VFS_PWRITE(fsp,
1265                                   aiblob.data,
1266                                   aiblob.length,
1267                                   0);
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;
1273                 return false;
1274         }
1275
1276         status = close_file(NULL, fsp, NORMAL_CLOSE);
1277         if (!NT_STATUS_IS_OK(status)) {
1278                 return false;
1279         }
1280         fsp = NULL;
1281
1282         return true;
1283 }
1284
1285 static bool ad_convert_truncate(vfs_handle_struct *handle,
1286                                 struct adouble *ad,
1287                                 const struct smb_filename *smb_fname)
1288 {
1289         int rc;
1290         off_t newlen;
1291
1292         newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1293
1294         rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1295         if (rc != 0) {
1296                 return false;
1297         }
1298
1299         return true;
1300 }
1301
1302 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1303                                    struct adouble *ad,
1304                                    uint32_t flags,
1305                                    bool *blank)
1306 {
1307         size_t rforklen = sizeof(empty_resourcefork);
1308         char buf[rforklen];
1309         ssize_t nread;
1310         int cmp;
1311         int rc;
1312
1313         *blank = false;
1314
1315         if (!(flags & AD_CONV_WIPE_BLANK)) {
1316                 return true;
1317         }
1318
1319         if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1320                 return true;
1321         }
1322
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));
1327                 return false;
1328         }
1329
1330         cmp = memcmp(buf, empty_resourcefork, rforklen);
1331         if (cmp != 0) {
1332                 return true;
1333         }
1334
1335         ad_setentrylen(ad, ADEID_RFORK, 0);
1336
1337         rc = ad_fset(handle, ad, ad->ad_fsp);
1338         if (rc != 0) {
1339                 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1340                 return false;
1341         }
1342
1343         *blank = true;
1344         return true;
1345 }
1346
1347 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1348                                 struct adouble *ad,
1349                                 struct files_struct *dirfsp,
1350                                 const struct smb_filename *smb_fname,
1351                                 uint32_t flags)
1352 {
1353         struct smb_filename *ad_name = NULL;
1354         int rc;
1355
1356         if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1357                 return true;
1358         }
1359
1360         if (!(flags & AD_CONV_DELETE)) {
1361                 return true;
1362         }
1363
1364         rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1365         if (rc != 0) {
1366                 return false;
1367         }
1368
1369         rc = SMB_VFS_NEXT_UNLINKAT(handle,
1370                         dirfsp,
1371                         ad_name,
1372                         0);
1373         if (rc != 0) {
1374                 DBG_ERR("Unlinking [%s] failed: %s\n",
1375                         smb_fname_str_dbg(ad_name), strerror(errno));
1376                 TALLOC_FREE(ad_name);
1377                 return false;
1378         }
1379
1380         DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1381         TALLOC_FREE(ad_name);
1382
1383         return true;
1384 }
1385
1386 /**
1387  * Convert from Apple's ._ file to Netatalk
1388  *
1389  * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1390  * bytes containing packed xattrs.
1391  *
1392  * @return -1 in case an error occurred, 0 if no conversion was done, 1
1393  * otherwise
1394  **/
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,
1399                 uint32_t flags)
1400 {
1401         struct adouble *ad = NULL;
1402         bool ok;
1403         bool converted_xattr = false;
1404         bool blank;
1405         int ret;
1406
1407         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1408         if (ad == NULL) {
1409                 return 0;
1410         }
1411
1412         ok = ad_convert_xattr(handle,
1413                               ad,
1414                               smb_fname,
1415                               catia_mappings,
1416                               &converted_xattr);
1417         if (!ok) {
1418                 ret = -1;
1419                 goto done;
1420         }
1421
1422         ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1423         if (!ok) {
1424                 ret = -1;
1425                 goto done;
1426         }
1427
1428         if (converted_xattr || blank) {
1429                 ok = ad_convert_truncate(handle, ad, smb_fname);
1430                 if (!ok) {
1431                         ret = -1;
1432                         goto done;
1433                 }
1434         }
1435
1436         ok = ad_convert_finderinfo(handle, ad, smb_fname);
1437         if (!ok) {
1438                 DBG_ERR("Failed to convert [%s]\n",
1439                         smb_fname_str_dbg(smb_fname));
1440                 ret = -1;
1441                 goto done;
1442         }
1443
1444         ok = ad_convert_delete_adfile(handle,
1445                         ad,
1446                         dirfsp,
1447                         smb_fname,
1448                         flags);
1449         if (!ok) {
1450                 ret = -1;
1451                 goto done;
1452         }
1453
1454         ret = 0;
1455 done:
1456         TALLOC_FREE(ad);
1457         return ret;
1458 }
1459
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)
1465 {
1466         files_struct *fsp = NULL;
1467         NTSTATUS status;
1468         int ret;
1469
1470         status = SMB_VFS_CREATE_FILE(
1471                 handle->conn,
1472                 NULL,                           /* req */
1473                 &handle->conn->cwd_fsp,         /* dirfsp */
1474                 adpath,
1475                 FILE_READ_DATA|FILE_WRITE_DATA,
1476                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1477                 FILE_OPEN_IF,
1478                 0,                              /* create_options */
1479                 0,                              /* file_attributes */
1480                 INTERNAL_OPEN_ONLY,
1481                 NULL,                           /* lease */
1482                 0,                              /* allocation_size */
1483                 0,                              /* private_flags */
1484                 NULL,                           /* sd */
1485                 NULL,                           /* ea_list */
1486                 &fsp,
1487                 NULL,                           /* info */
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));
1492                 return false;
1493         }
1494
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)
1497         {
1498                 ret = SMB_VFS_FCHOWN(fsp,
1499                                      smb_fname->st.st_ex_uid,
1500                                      smb_fname->st.st_ex_gid);
1501                 if (ret != 0) {
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);
1505                         return false;
1506                 }
1507         }
1508
1509         *_fsp = fsp;
1510         return true;
1511 }
1512
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)
1518 {
1519         files_struct *fsp = NULL;
1520         NTSTATUS status;
1521
1522         status = SMB_VFS_CREATE_FILE(
1523                 handle->conn,                           /* conn */
1524                 NULL,                                   /* req */
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 */
1529                         FILE_SHARE_DELETE),
1530                 FILE_OPEN,                              /* create_disposition*/
1531                 0,                                      /* create_options */
1532                 0,                                      /* file_attributes */
1533                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1534                 NULL,                                   /* lease */
1535                 0,                                      /* allocation_size */
1536                 0,                                      /* private_flags */
1537                 NULL,                                   /* sd */
1538                 NULL,                                   /* ea_list */
1539                 &fsp,                                   /* result */
1540                 NULL,                                   /* pinfo */
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),
1545                         nt_errstr(status));
1546                 return false;
1547         }
1548
1549         status = vfs_streaminfo(handle->conn,
1550                                 fsp,
1551                                 fsp->fsp_name,
1552                                 mem_ctx,
1553                                 num_streams,
1554                                 streams);
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),
1559                         nt_errstr(status));
1560                 return false;
1561         }
1562
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),
1567                         nt_errstr(status));
1568                 return false;
1569         }
1570
1571         return true;
1572 }
1573
1574 struct ad_collect_state {
1575         bool have_adfile;
1576         size_t adx_data_off;
1577         char *rsrc_data_buf;
1578 };
1579
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,
1584                                   struct adouble *ad,
1585                                   struct ad_collect_state *state)
1586 {
1587         struct smb_filename *sname = NULL;
1588         files_struct *fsp = NULL;
1589         struct ad_xattr_entry *e = NULL;
1590         char *mapped_name = NULL;
1591         char *p = NULL;
1592         size_t needed_size;
1593         ssize_t nread;
1594         NTSTATUS status;
1595         int ret;
1596         bool ok;
1597
1598         sname = synthetic_smb_fname(ad,
1599                                     smb_fname->base_name,
1600                                     stream->name,
1601                                     NULL,
1602                                     smb_fname->twrp,
1603                                     0);
1604         if (sname == NULL) {
1605                 return false;
1606         }
1607
1608         if (is_ntfs_default_stream_smb_fname(sname)) {
1609                 TALLOC_FREE(sname);
1610                 return true;
1611         }
1612
1613         DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1614
1615         ret = SMB_VFS_STAT(handle->conn, sname);
1616         if (ret != 0) {
1617                 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
1618                 ok = false;
1619                 goto out;
1620         }
1621
1622         status = SMB_VFS_CREATE_FILE(
1623                 handle->conn,
1624                 NULL,                           /* req */
1625                 &handle->conn->cwd_fsp,         /* dirfsp */
1626                 sname,
1627                 FILE_READ_DATA|DELETE_ACCESS,
1628                 FILE_SHARE_READ,
1629                 FILE_OPEN,
1630                 0,                              /* create_options */
1631                 0,                              /* file_attributes */
1632                 INTERNAL_OPEN_ONLY,             /* oplock_request */
1633                 NULL,                           /* lease */
1634                 0,                              /* allocation_size */
1635                 0,                              /* private_flags */
1636                 NULL,                           /* sd */
1637                 NULL,                           /* ea_list */
1638                 &fsp,
1639                 NULL,                           /* info */
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));
1644                 ok = false;
1645                 goto out;
1646         }
1647
1648         if (is_afpinfo_stream(stream->name)) {
1649                 char buf[AFP_INFO_SIZE];
1650
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));
1655                         ok = false;
1656                         goto out;
1657                 }
1658
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));
1664                         ok = false;
1665                         goto out;
1666                 }
1667
1668                 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1669                        buf + AFP_OFF_FinderInfo,
1670                        AFP_FinderSize);
1671
1672                 ok = set_delete_on_close(fsp,
1673                                          true,
1674                                          fsp->conn->session_info->security_token,
1675                                          fsp->conn->session_info->unix_token);
1676                 if (!ok) {
1677                         DBG_ERR("Deleting [%s] failed\n",
1678                                 smb_fname_str_dbg(sname));
1679                         ok = false;
1680                         goto out;
1681                 }
1682                 ok = true;
1683                 goto out;
1684         }
1685
1686         if (is_afpresource_stream(stream->name)) {
1687                 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1688                 if (ad->ad_rsrc_data == NULL) {
1689                         ok = false;
1690                         goto out;
1691                 }
1692
1693                 nread = SMB_VFS_PREAD(fsp,
1694                                       ad->ad_rsrc_data,
1695                                       stream->size,
1696                                       0);
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));
1701                         ok = false;
1702                         goto out;
1703                 }
1704
1705                 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1706
1707                 if (!state->have_adfile) {
1708                         /*
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
1712                          * resource stream.
1713                          */
1714                         ok = set_delete_on_close(
1715                                 fsp,
1716                                 true,
1717                                 fsp->conn->session_info->security_token,
1718                                 fsp->conn->session_info->unix_token);
1719                         if (!ok) {
1720                                 DBG_ERR("Deleting [%s] failed\n",
1721                                         smb_fname_str_dbg(sname));
1722                                 ok = false;
1723                                 goto out;
1724                         }
1725                 }
1726                 ok = true;
1727                 goto out;
1728         }
1729
1730         ad->adx_entries = talloc_realloc(ad,
1731                                          ad->adx_entries,
1732                                          struct ad_xattr_entry,
1733                                          ad->adx_header.adx_num_attrs + 1);
1734         if (ad->adx_entries == NULL) {
1735                 ok = false;
1736                 goto out;
1737         }
1738
1739         e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1740         *e = (struct ad_xattr_entry) {
1741                 .adx_length = stream->size,
1742         };
1743         e->adx_name = talloc_strdup(ad, stream->name + 1);
1744         if (e->adx_name == NULL) {
1745                 ok = false;
1746                 goto out;
1747         }
1748         p = strchr(e->adx_name, ':');
1749         if (p != NULL) {
1750                 *p = '\0';
1751         }
1752
1753         status = string_replace_allocate(handle->conn,
1754                                          e->adx_name,
1755                                          cmaps,
1756                                          ad,
1757                                          &mapped_name,
1758                                          vfs_translate_to_unix);
1759         if (!NT_STATUS_IS_OK(status) &&
1760             !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1761         {
1762                 DBG_ERR("string_replace_allocate failed\n");
1763                 ok = false;
1764                 goto out;
1765         }
1766
1767         e->adx_name = mapped_name;
1768         e->adx_namelen = strlen(e->adx_name) + 1,
1769
1770         DBG_DEBUG("%u: name (%s) size (%zu)\n",
1771                   ad->adx_header.adx_num_attrs,
1772                   e->adx_name,
1773                   (size_t)e->adx_length);
1774
1775         ad->adx_header.adx_num_attrs++;
1776
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,
1780                                               ad->adx_data,
1781                                               char,
1782                                               needed_size);
1783                 if (ad->adx_data == NULL) {
1784                         ok = false;
1785                         goto out;
1786                 }
1787         }
1788
1789         nread = SMB_VFS_PREAD(fsp,
1790                               ad->adx_data + state->adx_data_off,
1791                               stream->size,
1792                               0);
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));
1797                 ok = false;
1798                 goto out;
1799         }
1800         state->adx_data_off += nread;
1801
1802         ok = set_delete_on_close(fsp,
1803                                  true,
1804                                  fsp->conn->session_info->security_token,
1805                                  fsp->conn->session_info->unix_token);
1806         if (!ok) {
1807                 DBG_ERR("Deleting [%s] failed\n",
1808                         smb_fname_str_dbg(sname));
1809                 ok = false;
1810                 goto out;
1811         }
1812
1813 out:
1814         TALLOC_FREE(sname);
1815         if (fsp != NULL) {
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),
1820                                 nt_errstr(status));
1821                         ok = false;
1822                 }
1823         }
1824
1825         return ok;
1826 }
1827
1828 /**
1829  * Convert filesystem metadata to AppleDouble file
1830  **/
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,
1835                   bool *converted)
1836 {
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;
1847         size_t i;
1848         NTSTATUS status;
1849         int ret;
1850         bool ok;
1851
1852         *converted = false;
1853
1854         if (cmaps == NULL) {
1855                 const char **mappings = NULL;
1856
1857                 mappings = str_list_make_v3_const(
1858                         frame, catia_mappings, NULL);
1859                 if (mappings == NULL) {
1860                         ok = false;
1861                         goto out;
1862                 }
1863                 cmaps = string_replace_init_map(mem_ctx, mappings);
1864                 TALLOC_FREE(mappings);
1865         }
1866
1867         ok = ad_unconvert_get_streams(handle,
1868                                       smb_fname,
1869                                       frame,
1870                                       &num_streams,
1871                                       &streams);
1872         if (!ok) {
1873                 goto out;
1874         }
1875
1876         for (i = 0; i < num_streams; i++) {
1877                 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
1878                         continue;
1879                 }
1880                 to_convert++;
1881                 if (is_afpresource_stream(streams[i].name)) {
1882                         have_rsrc = true;
1883                 }
1884         }
1885
1886         if (to_convert == 0) {
1887                 ok = true;
1888                 goto out;
1889         }
1890
1891         state = (struct ad_collect_state) {
1892                 .adx_data_off = 0,
1893         };
1894
1895         ret = adouble_path(frame, smb_fname, &adpath);
1896         if (ret != 0) {
1897                 ok = false;
1898                 goto out;
1899         }
1900
1901         ret = SMB_VFS_STAT(handle->conn, adpath);
1902         if (ret == 0) {
1903                 state.have_adfile = true;
1904         } else {
1905                 if (errno != ENOENT) {
1906                         ok = false;
1907                         goto out;
1908                 }
1909                 state.have_adfile = false;
1910         }
1911
1912         if (to_convert == 1 && have_rsrc && state.have_adfile) {
1913                 /*
1914                  * So we have just a single stream, the resource fork stream
1915                  * from an AppleDouble file. Fine, that means there's nothing to
1916                  * convert.
1917                  */
1918                 ok = true;
1919                 goto out;
1920         }
1921
1922         ad = ad_init(frame, ADOUBLE_RSRC);
1923         if (ad == NULL) {
1924                 ok = false;
1925                 goto out;
1926         }
1927
1928         for (i = 0; i < num_streams; i++) {
1929                 ok = ad_collect_one_stream(handle,
1930                                            cmaps,
1931                                            smb_fname,
1932                                            &streams[i],
1933                                            ad,
1934                                            &state);
1935                 if (!ok) {
1936                         goto out;
1937                 }
1938         }
1939
1940         ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
1941         if (!ok) {
1942                 DBG_ERR("Failed to open adfile [%s]\n",
1943                         smb_fname_str_dbg(smb_fname));
1944                 goto out;
1945         }
1946
1947         ret = ad_fset(handle, ad, fsp);
1948         if (ret != 0) {
1949                 ok = false;
1950                 goto out;
1951         }
1952
1953         *converted = true;
1954         ok = true;
1955
1956 out:
1957         if (fsp != NULL) {
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),
1962                                 nt_errstr(status));
1963                         ok = false;
1964                 }
1965         }
1966         TALLOC_FREE(frame);
1967         return ok;
1968 }
1969
1970 /**
1971  * Read and parse Netatalk AppleDouble metadata xattr
1972  **/
1973 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1974                             struct adouble *ad,
1975                             const struct smb_filename *smb_fname)
1976 {
1977         int      rc = 0;
1978         ssize_t  ealen;
1979         bool     ok;
1980
1981         DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1982
1983         ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1984                                  AFPINFO_EA_NETATALK, ad->ad_data,
1985                                  AD_DATASZ_XATTR);
1986         if (ealen == -1) {
1987                 switch (errno) {
1988                 case ENOATTR:
1989                 case ENOENT:
1990                         if (errno == ENOATTR) {
1991                                 errno = ENOENT;
1992                         }
1993                         rc = -1;
1994                         goto exit;
1995                 default:
1996                         DEBUG(2, ("error reading meta xattr: %s\n",
1997                                   strerror(errno)));
1998                         rc = -1;
1999                         goto exit;
2000                 }
2001         }
2002         if (ealen != AD_DATASZ_XATTR) {
2003                 DEBUG(2, ("bad size %zd\n", ealen));
2004                 errno = EINVAL;
2005                 rc = -1;
2006                 goto exit;
2007         }
2008
2009         /* Now parse entries */
2010         ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2011         if (!ok) {
2012                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2013                 errno = EINVAL;
2014                 rc = -1;
2015                 goto exit;
2016         }
2017
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"));
2027                 errno = EINVAL;
2028                 rc = -1;
2029                 goto exit;
2030         }
2031
2032 exit:
2033         DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2034                 smb_fname->base_name, rc));
2035
2036         if (rc != 0) {
2037                 ealen = -1;
2038                 if (errno == EINVAL) {
2039                         become_root();
2040                         (void)SMB_VFS_REMOVEXATTR(handle->conn,
2041                                                   smb_fname,
2042                                                   AFPINFO_EA_NETATALK);
2043                         unbecome_root();
2044                         errno = ENOENT;
2045                 }
2046         }
2047         return ealen;
2048 }
2049
2050 static int ad_open_rsrc(vfs_handle_struct *handle,
2051                         const struct smb_filename *smb_fname,
2052                         int flags,
2053                         mode_t mode,
2054                         files_struct **_fsp)
2055 {
2056         int ret;
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;
2062         NTSTATUS status;
2063
2064         ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2065         if (ret != 0) {
2066                 return -1;
2067         }
2068
2069         ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
2070         if (ret != 0) {
2071                 TALLOC_FREE(adp_smb_fname);
2072                 return -1;
2073         }
2074
2075         access_mask = FILE_GENERIC_READ;
2076         share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
2077         create_disposition = FILE_OPEN;
2078
2079         if (flags & O_RDWR) {
2080                 access_mask |= FILE_GENERIC_WRITE;
2081                 share_access &= ~FILE_SHARE_WRITE;
2082         }
2083
2084         status = SMB_VFS_CREATE_FILE(
2085                 handle->conn,                   /* conn */
2086                 NULL,                           /* req */
2087                 &handle->conn->cwd_fsp,         /* dirfsp */
2088                 adp_smb_fname,
2089                 access_mask,
2090                 share_access,
2091                 create_disposition,
2092                 0,                              /* create_options */
2093                 0,                              /* file_attributes */
2094                 INTERNAL_OPEN_ONLY,             /* oplock_request */
2095                 NULL,                           /* lease */
2096                 0,                              /* allocation_size */
2097                 0,                              /* private_flags */
2098                 NULL,                           /* sd */
2099                 NULL,                           /* ea_list */
2100                 &fsp,
2101                 NULL,                           /* psbuf */
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");
2106                 return -1;
2107         }
2108
2109         *_fsp = fsp;
2110         return 0;
2111 }
2112
2113 /*
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.
2117  */
2118 static int ad_open(vfs_handle_struct *handle,
2119                    struct adouble *ad,
2120                    files_struct *fsp,
2121                    const struct smb_filename *smb_fname,
2122                    int flags,
2123                    mode_t mode)
2124 {
2125         int ret;
2126
2127         DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2128                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2129
2130         if (ad->ad_type == ADOUBLE_META) {
2131                 return 0;
2132         }
2133
2134         if (fsp != NULL) {
2135                 ad->ad_fsp = fsp;
2136                 ad->ad_opened = false;
2137                 return 0;
2138         }
2139
2140         ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
2141         if (ret != 0) {
2142                 return -1;
2143         }
2144         ad->ad_opened = true;
2145
2146         DBG_DEBUG("Path [%s] type [%s]\n",
2147                   smb_fname->base_name,
2148                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2149
2150         return 0;
2151 }
2152
2153 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2154                                     struct adouble *ad,
2155                                     const struct smb_filename *smb_fname)
2156 {
2157         size_t to_read;
2158         ssize_t len;
2159         int ret;
2160         bool ok;
2161
2162         ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2163         if (ret != 0) {
2164                 DBG_ERR("fstat [%s] failed: %s\n",
2165                         fsp_str_dbg(ad->ad_fsp), strerror(errno));
2166                 return -1;
2167         }
2168
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;
2172         }
2173
2174         len = SMB_VFS_NEXT_PREAD(handle,
2175                                  ad->ad_fsp,
2176                                  ad->ad_data,
2177                                  to_read,
2178                                  0);
2179         if (len != to_read)  {
2180                 DBG_NOTICE("%s %s: bad size: %zd\n",
2181                            smb_fname->base_name, strerror(errno), len);
2182                 return -1;
2183         }
2184
2185         /* Now parse entries */
2186         ok = ad_unpack(ad,
2187                        ADEID_NUM_DOT_UND,
2188                        ad->ad_fsp->fsp_name->st.st_ex_size);
2189         if (!ok) {
2190                 DBG_ERR("invalid AppleDouble resource %s\n",
2191                         smb_fname->base_name);
2192                 errno = EINVAL;
2193                 return -1;
2194         }
2195
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))
2199         {
2200                 DBG_ERR("invalid AppleDouble resource %s\n",
2201                         smb_fname->base_name);
2202                 errno = EINVAL;
2203                 return -1;
2204         }
2205
2206         return len;
2207 }
2208
2209 /**
2210  * Read and parse resource fork, either ._ AppleDouble file or xattr
2211  **/
2212 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2213                             struct adouble *ad,
2214                             const struct smb_filename *smb_fname)
2215 {
2216         return ad_read_rsrc_adouble(handle, ad, smb_fname);
2217 }
2218
2219 /**
2220  * Read and unpack an AppleDouble metadata xattr or resource
2221  **/
2222 static ssize_t ad_read(vfs_handle_struct *handle,
2223                        struct adouble *ad,
2224                        const struct smb_filename *smb_fname)
2225 {
2226         switch (ad->ad_type) {
2227         case ADOUBLE_META:
2228                 return ad_read_meta(handle, ad, smb_fname);
2229         case ADOUBLE_RSRC:
2230                 return ad_read_rsrc(handle, ad, smb_fname);
2231         default:
2232                 return -1;
2233         }
2234 }
2235
2236 static int adouble_destructor(struct adouble *ad)
2237 {
2238         NTSTATUS status;
2239
2240         if (!ad->ad_opened) {
2241                 return 0;
2242         }
2243
2244         SMB_ASSERT(ad->ad_fsp != NULL);
2245
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));
2250         }
2251
2252         return 0;
2253 }
2254
2255 /**
2256  * Allocate a struct adouble without initialiing it
2257  *
2258  * The struct is either hang of the fsp extension context or if fsp is
2259  * NULL from ctx.
2260  *
2261  * @param[in] ctx        talloc context
2262  * @param[in] handle     vfs handle
2263  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2264  *
2265  * @return               adouble handle
2266  **/
2267 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2268                                 adouble_type_t type)
2269 {
2270         int rc = 0;
2271         size_t adsize = 0;
2272         struct adouble *ad;
2273
2274         switch (type) {
2275         case ADOUBLE_META:
2276                 adsize = AD_DATASZ_XATTR;
2277                 break;
2278         case ADOUBLE_RSRC:
2279                 /*
2280                  * AppleDouble ._ file case, optimize for fewer (but larger)
2281                  * IOs. Two cases:
2282                  *
2283                  * - without xattrs size of the header is exactly
2284                  *   AD_DATASZ_DOT_UND (82) bytes
2285                  *
2286                  * - with embedded xattrs it can be larger, up to
2287                  *   AD_XATTR_MAX_HDR_SIZE
2288                  *
2289                  * Larger headers are not supported, but this is a reasonable
2290                  * limit that is also employed by the macOS client.
2291                  *
2292                  * We used the largest possible size to be able to read the full
2293                  * header with one IO.
2294                  */
2295                 adsize = AD_XATTR_MAX_HDR_SIZE;
2296                 break;
2297         default:
2298                 return NULL;
2299         }
2300
2301         ad = talloc_zero(ctx, struct adouble);
2302         if (ad == NULL) {
2303                 rc = -1;
2304                 goto exit;
2305         }
2306
2307         if (adsize) {
2308                 ad->ad_data = talloc_zero_array(ad, char, adsize);
2309                 if (ad->ad_data == NULL) {
2310                         rc = -1;
2311                         goto exit;
2312                 }
2313         }
2314
2315         ad->ad_type = type;
2316         ad->ad_magic = AD_MAGIC;
2317         ad->ad_version = AD_VERSION;
2318
2319         talloc_set_destructor(ad, adouble_destructor);
2320
2321 exit:
2322         if (rc != 0) {
2323                 TALLOC_FREE(ad);
2324         }
2325         return ad;
2326 }
2327
2328 /**
2329  * Allocate and initialize a new struct adouble
2330  *
2331  * @param[in] ctx        talloc context
2332  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2333  *
2334  * @return               adouble handle, initialized
2335  **/
2336 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2337 {
2338         int rc = 0;
2339         const struct ad_entry_order  *eid;
2340         struct adouble *ad = NULL;
2341         time_t t = time(NULL);
2342
2343         switch (type) {
2344         case ADOUBLE_META:
2345                 eid = entry_order_meta_xattr;
2346                 break;
2347         case ADOUBLE_RSRC:
2348                 eid = entry_order_dot_und;
2349                 break;
2350         default:
2351                 return NULL;
2352         }
2353
2354         ad = ad_alloc(ctx, type);
2355         if (ad == NULL) {
2356                 return NULL;
2357         }
2358
2359         while (eid->id) {
2360                 ad->ad_eid[eid->id].ade_off = eid->offset;
2361                 ad->ad_eid[eid->id].ade_len = eid->len;
2362                 eid++;
2363         }
2364
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));
2370
2371         if (rc != 0) {
2372                 TALLOC_FREE(ad);
2373         }
2374         return ad;
2375 }
2376
2377 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2378                                        vfs_handle_struct *handle,
2379                                        files_struct *fsp,
2380                                        const struct smb_filename *smb_fname,
2381                                        adouble_type_t type)
2382 {
2383         int rc = 0;
2384         ssize_t len;
2385         struct adouble *ad = NULL;
2386         int mode;
2387
2388         if (fsp != NULL) {
2389                 smb_fname = fsp->base_fsp->fsp_name;
2390         }
2391
2392         DEBUG(10, ("ad_get(%s) called for %s\n",
2393                    type == ADOUBLE_META ? "meta" : "rsrc",
2394                    smb_fname != NULL ? smb_fname->base_name : "???"));
2395
2396         ad = ad_alloc(ctx, type);
2397         if (ad == NULL) {
2398                 rc = -1;
2399                 goto exit;
2400         }
2401
2402         /* Try rw first so we can use the fd in ad_convert() */
2403         mode = O_RDWR;
2404
2405         rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2406         if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2407                 mode = O_RDONLY;
2408                 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2409         }
2410         if (rc == -1) {
2411                 DBG_DEBUG("ad_open [%s] error [%s]\n",
2412                           smb_fname->base_name, strerror(errno));
2413                 goto exit;
2414
2415         }
2416
2417         len = ad_read(handle, ad, smb_fname);
2418         if (len == -1) {
2419                 DEBUG(10, ("error reading AppleDouble for %s\n",
2420                         smb_fname->base_name));
2421                 rc = -1;
2422                 goto exit;
2423         }
2424
2425 exit:
2426         DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2427                   type == ADOUBLE_META ? "meta" : "rsrc",
2428                   smb_fname->base_name, rc));
2429
2430         if (rc != 0) {
2431                 TALLOC_FREE(ad);
2432         }
2433         return ad;
2434 }
2435
2436 /**
2437  * Return AppleDouble data for a file
2438  *
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
2443  *
2444  * @return             talloced struct adouble or NULL on error
2445  **/
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)
2450 {
2451         return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2452 }
2453
2454 /**
2455  * Return AppleDouble data for a file
2456  *
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
2461  *
2462  * @return             talloced struct adouble or NULL on error
2463  **/
2464 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2465                         files_struct *fsp, adouble_type_t type)
2466 {
2467         return ad_get_internal(ctx, handle, fsp, NULL, type);
2468 }
2469
2470 /**
2471  * Set AppleDouble metadata on a file or directory
2472  *
2473  * @param[in] ad      adouble handle
2474  *
2475  * @param[in] smb_fname    pathname to file or directory
2476  *
2477  * @return            status code, 0 means success
2478  **/
2479 int ad_set(vfs_handle_struct *handle,
2480            struct adouble *ad,
2481            const struct smb_filename *smb_fname)
2482 {
2483         bool ok;
2484         int ret;
2485
2486         DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2487
2488         if (ad->ad_type != ADOUBLE_META) {
2489                 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2490                         smb_fname->base_name);
2491                 return -1;
2492         }
2493
2494         ok = ad_pack(handle, ad, NULL);
2495         if (!ok) {
2496                 return -1;
2497         }
2498
2499         ret = SMB_VFS_SETXATTR(handle->conn,
2500                                smb_fname,
2501                                AFPINFO_EA_NETATALK,
2502                                ad->ad_data,
2503                                AD_DATASZ_XATTR, 0);
2504
2505         DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2506
2507         return ret;
2508 }
2509
2510 /**
2511  * Set AppleDouble metadata on a file or directory
2512  *
2513  * @param[in] ad      adouble handle
2514  * @param[in] fsp     file handle
2515  *
2516  * @return            status code, 0 means success
2517  **/
2518 int ad_fset(struct vfs_handle_struct *handle,
2519             struct adouble *ad,
2520             files_struct *fsp)
2521 {
2522         int rc = -1;
2523         ssize_t len;
2524         bool ok;
2525
2526         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2527
2528         if ((fsp == NULL)
2529             || (fsp->fh == NULL)
2530             || (fsp->fh->fd == -1))
2531         {
2532                 smb_panic("bad fsp");
2533         }
2534
2535         ok = ad_pack(handle, ad, fsp);
2536         if (!ok) {
2537                 return -1;
2538         }
2539
2540         switch (ad->ad_type) {
2541         case ADOUBLE_META:
2542                 rc = SMB_VFS_NEXT_SETXATTR(handle,
2543                                            fsp->fsp_name,
2544                                            AFPINFO_EA_NETATALK,
2545                                            ad->ad_data,
2546                                            AD_DATASZ_XATTR, 0);
2547                 break;
2548
2549         case ADOUBLE_RSRC:
2550                 len = SMB_VFS_NEXT_PWRITE(handle,
2551                                           fsp,
2552                                           ad->ad_data,
2553                                           ad_getentryoff(ad, ADEID_RFORK),
2554                                           0);
2555                 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2556                         DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2557                         return -1;
2558                 }
2559                 rc = 0;
2560                 break;
2561
2562         default:
2563                 return -1;
2564         }
2565
2566         DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2567
2568         return rc;
2569 }
2570
2571 bool is_adouble_file(const char *path)
2572 {
2573         const char *p = NULL;
2574         int match;
2575
2576         p = strrchr(path, '/');
2577         if (p == NULL) {
2578                 p = path;
2579         } else {
2580                 p++;
2581         }
2582
2583         match = strncmp(p,
2584                         ADOUBLE_NAME_PREFIX,
2585                         strlen(ADOUBLE_NAME_PREFIX));
2586         if (match != 0) {
2587                 return false;
2588         }
2589         return true;
2590 }
2591
2592 /**
2593  * Prepend "._" to a basename
2594  * Return a new struct smb_filename with stream_name == NULL.
2595  **/
2596 int adouble_path(TALLOC_CTX *ctx,
2597                  const struct smb_filename *smb_fname_in,
2598                  struct smb_filename **pp_smb_fname_out)
2599 {
2600         char *parent;
2601         const char *base;
2602         struct smb_filename *smb_fname = cp_smb_filename(ctx,
2603                                                 smb_fname_in);
2604
2605         if (smb_fname == NULL) {
2606                 return -1;
2607         }
2608
2609         /* We need streamname to be NULL */
2610         TALLOC_FREE(smb_fname->stream_name);
2611
2612         /* And we're replacing base_name. */
2613         TALLOC_FREE(smb_fname->base_name);
2614
2615         SET_STAT_INVALID(smb_fname->st);
2616
2617         if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2618                                 &parent, &base)) {
2619                 TALLOC_FREE(smb_fname);
2620                 return -1;
2621         }
2622
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);
2627                 return -1;
2628         }
2629
2630         *pp_smb_fname_out = smb_fname;
2631
2632         return 0;
2633 }
2634
2635 /**
2636  * Allocate and initialize an AfpInfo struct
2637  **/
2638 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2639 {
2640         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2641         if (ai == NULL) {
2642                 return NULL;
2643         }
2644         ai->afpi_Signature = AFP_Signature;
2645         ai->afpi_Version = AFP_Version;
2646         ai->afpi_BackupTime = AD_DATE_START;
2647         return ai;
2648 }
2649
2650 /**
2651  * Pack an AfpInfo struct into a buffer
2652  *
2653  * Buffer size must be at least AFP_INFO_SIZE
2654  * Returns size of packed buffer
2655  **/
2656 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2657 {
2658         memset(buf, 0, AFP_INFO_SIZE);
2659
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));
2664
2665         return AFP_INFO_SIZE;
2666 }
2667
2668 /**
2669  * Unpack a buffer into a AfpInfo structure
2670  *
2671  * Buffer size must be at least AFP_INFO_SIZE
2672  * Returns allocated AfpInfo struct
2673  **/
2674 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2675 {
2676         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2677         if (ai == NULL) {
2678                 return NULL;
2679         }
2680
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));
2686
2687         if (ai->afpi_Signature != AFP_Signature
2688             || ai->afpi_Version != AFP_Version) {
2689                 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2690                 TALLOC_FREE(ai);
2691         }
2692
2693         return ai;
2694 }