python:tests: Store keys as bytes rather than as lists of ints
[samba.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->flags);
1099                 TALLOC_FREE(mapped_name);
1100                 if (stream_name == NULL) {
1101                         DBG_ERR("synthetic_smb_fname failed\n");
1102                         ok = false;
1103                         goto fail;
1104                 }
1105
1106                 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1107
1108                 status = SMB_VFS_CREATE_FILE(
1109                         handle->conn,                   /* conn */
1110                         NULL,                           /* req */
1111                         0,                              /* root_dir_fid */
1112                         stream_name,                    /* fname */
1113                         FILE_GENERIC_WRITE,             /* access_mask */
1114                         FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1115                         FILE_OPEN_IF,                   /* create_disposition */
1116                         0,                              /* create_options */
1117                         0,                              /* file_attributes */
1118                         INTERNAL_OPEN_ONLY,             /* oplock_request */
1119                         NULL,                           /* lease */
1120                         0,                              /* allocation_size */
1121                         0,                              /* private_flags */
1122                         NULL,                           /* sd */
1123                         NULL,                           /* ea_list */
1124                         &fsp,                           /* result */
1125                         NULL,                           /* psbuf */
1126                         NULL, NULL);                    /* create context */
1127                 TALLOC_FREE(stream_name);
1128                 if (!NT_STATUS_IS_OK(status)) {
1129                         DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1130                         ok = false;
1131                         goto fail;
1132                 }
1133
1134                 nwritten = SMB_VFS_PWRITE(fsp,
1135                                           ad->ad_data + e->adx_offset,
1136                                           e->adx_length,
1137                                           0);
1138                 if (nwritten == -1) {
1139                         DBG_ERR("SMB_VFS_PWRITE failed\n");
1140                         saved_errno = errno;
1141                         close_file(NULL, fsp, ERROR_CLOSE);
1142                         errno = saved_errno;
1143                         ok = false;
1144                         goto fail;
1145                 }
1146
1147                 status = close_file(NULL, fsp, NORMAL_CLOSE);
1148                 if (!NT_STATUS_IS_OK(status)) {
1149                         ok = false;
1150                         goto fail;
1151                 }
1152                 fsp = NULL;
1153         }
1154
1155         ad->adx_header.adx_num_attrs = 0;
1156         TALLOC_FREE(ad->adx_entries);
1157
1158         ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1159
1160         rc = ad_fset(handle, ad, ad->ad_fsp);
1161         if (rc != 0) {
1162                 DBG_ERR("ad_fset on [%s] failed: %s\n",
1163                         fsp_str_dbg(ad->ad_fsp), strerror(errno));
1164                 ok = false;
1165                 goto fail;
1166         }
1167
1168         ok = ad_convert_move_reso(handle, ad, smb_fname);
1169         if (!ok) {
1170                 goto fail;
1171         }
1172
1173         *converted_xattr = true;
1174         ok = true;
1175
1176 fail:
1177         return ok;
1178 }
1179
1180 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1181                                   struct adouble *ad,
1182                                   const struct smb_filename *smb_fname)
1183 {
1184         char *p_ad = NULL;
1185         AfpInfo *ai = NULL;
1186         DATA_BLOB aiblob;
1187         struct smb_filename *stream_name = NULL;
1188         files_struct *fsp = NULL;
1189         size_t size;
1190         ssize_t nwritten;
1191         NTSTATUS status;
1192         int saved_errno = 0;
1193         int cmp;
1194
1195         cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1196         if (cmp != 0) {
1197                 return true;
1198         }
1199
1200         p_ad = ad_get_entry(ad, ADEID_FINDERI);
1201         if (p_ad == NULL) {
1202                 return false;
1203         }
1204
1205         ai = afpinfo_new(talloc_tos());
1206         if (ai == NULL) {
1207                 return false;
1208         }
1209
1210         memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1211
1212         aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1213         if (aiblob.data == NULL) {
1214                 TALLOC_FREE(ai);
1215                 return false;
1216         }
1217
1218         size = afpinfo_pack(ai, (char *)aiblob.data);
1219         TALLOC_FREE(ai);
1220         if (size != AFP_INFO_SIZE) {
1221                 return false;
1222         }
1223
1224         stream_name = synthetic_smb_fname(talloc_tos(),
1225                                           smb_fname->base_name,
1226                                           AFPINFO_STREAM,
1227                                           NULL,
1228                                           smb_fname->flags);
1229         if (stream_name == NULL) {
1230                 data_blob_free(&aiblob);
1231                 DBG_ERR("synthetic_smb_fname failed\n");
1232                 return false;
1233         }
1234
1235         DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1236
1237         status = SMB_VFS_CREATE_FILE(
1238                 handle->conn,                   /* conn */
1239                 NULL,                           /* req */
1240                 0,                              /* root_dir_fid */
1241                 stream_name,                    /* fname */
1242                 FILE_GENERIC_WRITE,             /* access_mask */
1243                 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1244                 FILE_OPEN_IF,                   /* create_disposition */
1245                 0,                              /* create_options */
1246                 0,                              /* file_attributes */
1247                 INTERNAL_OPEN_ONLY,             /* oplock_request */
1248                 NULL,                           /* lease */
1249                 0,                              /* allocation_size */
1250                 0,                              /* private_flags */
1251                 NULL,                           /* sd */
1252                 NULL,                           /* ea_list */
1253                 &fsp,                           /* result */
1254                 NULL,                           /* psbuf */
1255                 NULL, NULL);                    /* create context */
1256         TALLOC_FREE(stream_name);
1257         if (!NT_STATUS_IS_OK(status)) {
1258                 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1259                 return false;
1260         }
1261
1262         nwritten = SMB_VFS_PWRITE(fsp,
1263                                   aiblob.data,
1264                                   aiblob.length,
1265                                   0);
1266         if (nwritten == -1) {
1267                 DBG_ERR("SMB_VFS_PWRITE failed\n");
1268                 saved_errno = errno;
1269                 close_file(NULL, fsp, ERROR_CLOSE);
1270                 errno = saved_errno;
1271                 return false;
1272         }
1273
1274         status = close_file(NULL, fsp, NORMAL_CLOSE);
1275         if (!NT_STATUS_IS_OK(status)) {
1276                 return false;
1277         }
1278         fsp = NULL;
1279
1280         return true;
1281 }
1282
1283 static bool ad_convert_truncate(vfs_handle_struct *handle,
1284                                 struct adouble *ad,
1285                                 const struct smb_filename *smb_fname)
1286 {
1287         int rc;
1288         off_t newlen;
1289
1290         newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1291
1292         rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1293         if (rc != 0) {
1294                 return false;
1295         }
1296
1297         return true;
1298 }
1299
1300 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1301                                    struct adouble *ad,
1302                                    uint32_t flags,
1303                                    bool *blank)
1304 {
1305         size_t rforklen = sizeof(empty_resourcefork);
1306         char buf[rforklen];
1307         ssize_t nread;
1308         int cmp;
1309         int rc;
1310
1311         *blank = false;
1312
1313         if (!(flags & AD_CONV_WIPE_BLANK)) {
1314                 return true;
1315         }
1316
1317         if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1318                 return true;
1319         }
1320
1321         nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1322         if (nread != rforklen) {
1323                 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1324                         rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1325                 return false;
1326         }
1327
1328         cmp = memcmp(buf, empty_resourcefork, rforklen);
1329         if (cmp != 0) {
1330                 return true;
1331         }
1332
1333         ad_setentrylen(ad, ADEID_RFORK, 0);
1334
1335         rc = ad_fset(handle, ad, ad->ad_fsp);
1336         if (rc != 0) {
1337                 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1338                 return false;
1339         }
1340
1341         *blank = true;
1342         return true;
1343 }
1344
1345 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1346                                      struct adouble *ad,
1347                                      const struct smb_filename *smb_fname,
1348                                      uint32_t flags)
1349 {
1350         struct smb_filename *ad_name = NULL;
1351         int rc;
1352
1353         if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1354                 return true;
1355         }
1356
1357         if (!(flags & AD_CONV_DELETE)) {
1358                 return true;
1359         }
1360
1361         rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1362         if (rc != 0) {
1363                 return false;
1364         }
1365
1366         rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1367         if (rc != 0) {
1368                 DBG_ERR("Unlinking [%s] failed: %s\n",
1369                         smb_fname_str_dbg(ad_name), strerror(errno));
1370                 TALLOC_FREE(ad_name);
1371                 return false;
1372         }
1373
1374         DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1375         TALLOC_FREE(ad_name);
1376
1377         return true;
1378 }
1379
1380 /**
1381  * Convert from Apple's ._ file to Netatalk
1382  *
1383  * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1384  * bytes containing packed xattrs.
1385  *
1386  * @return -1 in case an error occurred, 0 if no conversion was done, 1
1387  * otherwise
1388  **/
1389 int ad_convert(struct vfs_handle_struct *handle,
1390                const struct smb_filename *smb_fname,
1391                const char *catia_mappings,
1392                uint32_t flags)
1393 {
1394         struct adouble *ad = NULL;
1395         bool ok;
1396         bool converted_xattr = false;
1397         bool blank;
1398         int ret;
1399
1400         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1401         if (ad == NULL) {
1402                 return 0;
1403         }
1404
1405         ok = ad_convert_xattr(handle,
1406                               ad,
1407                               smb_fname,
1408                               catia_mappings,
1409                               &converted_xattr);
1410         if (!ok) {
1411                 ret = -1;
1412                 goto done;
1413         }
1414
1415         ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1416         if (!ok) {
1417                 ret = -1;
1418                 goto done;
1419         }
1420
1421         if (converted_xattr || blank) {
1422                 ok = ad_convert_truncate(handle, ad, smb_fname);
1423                 if (!ok) {
1424                         ret = -1;
1425                         goto done;
1426                 }
1427         }
1428
1429         ok = ad_convert_finderinfo(handle, ad, smb_fname);
1430         if (!ok) {
1431                 DBG_ERR("Failed to convert [%s]\n",
1432                         smb_fname_str_dbg(smb_fname));
1433                 ret = -1;
1434                 goto done;
1435         }
1436
1437         ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
1438         if (!ok) {
1439                 ret = -1;
1440                 goto done;
1441         }
1442
1443         ret = 0;
1444 done:
1445         TALLOC_FREE(ad);
1446         return ret;
1447 }
1448
1449 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1450                                  struct vfs_handle_struct *handle,
1451                                  struct smb_filename *smb_fname,
1452                                  struct smb_filename *adpath,
1453                                  files_struct **_fsp)
1454 {
1455         files_struct *fsp = NULL;
1456         NTSTATUS status;
1457         int ret;
1458
1459         status = SMB_VFS_CREATE_FILE(
1460                 handle->conn,
1461                 NULL,                           /* req */
1462                 0,                              /* root_dir_fid */
1463                 adpath,
1464                 FILE_READ_DATA|FILE_WRITE_DATA,
1465                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1466                 FILE_OPEN_IF,
1467                 0,                              /* create_options */
1468                 0,                              /* file_attributes */
1469                 INTERNAL_OPEN_ONLY,
1470                 NULL,                           /* lease */
1471                 0,                              /* allocation_size */
1472                 0,                              /* private_flags */
1473                 NULL,                           /* sd */
1474                 NULL,                           /* ea_list */
1475                 &fsp,
1476                 NULL,                           /* info */
1477                 NULL, NULL);                    /* create context */
1478         if (!NT_STATUS_IS_OK(status)) {
1479                 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1480                         smb_fname_str_dbg(adpath), nt_errstr(status));
1481                 return false;
1482         }
1483
1484         if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1485             fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1486         {
1487                 ret = SMB_VFS_FCHOWN(fsp,
1488                                      smb_fname->st.st_ex_uid,
1489                                      smb_fname->st.st_ex_gid);
1490                 if (ret != 0) {
1491                         DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1492                                 fsp_str_dbg(fsp), nt_errstr(status));
1493                         close_file(NULL, fsp, NORMAL_CLOSE);
1494                         return false;
1495                 }
1496         }
1497
1498         *_fsp = fsp;
1499         return true;
1500 }
1501
1502 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1503                                      struct smb_filename *smb_fname,
1504                                      TALLOC_CTX *mem_ctx,
1505                                      unsigned int *num_streams,
1506                                      struct stream_struct **streams)
1507 {
1508         files_struct *fsp = NULL;
1509         NTSTATUS status;
1510
1511         status = SMB_VFS_CREATE_FILE(
1512                 handle->conn,                           /* conn */
1513                 NULL,                                   /* req */
1514                 0,                                      /* root_dir_fid */
1515                 smb_fname,                              /* fname */
1516                 FILE_READ_ATTRIBUTES,                   /* access_mask */
1517                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
1518                         FILE_SHARE_DELETE),
1519                 FILE_OPEN,                              /* create_disposition*/
1520                 0,                                      /* create_options */
1521                 0,                                      /* file_attributes */
1522                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
1523                 NULL,                                   /* lease */
1524                 0,                                      /* allocation_size */
1525                 0,                                      /* private_flags */
1526                 NULL,                                   /* sd */
1527                 NULL,                                   /* ea_list */
1528                 &fsp,                                   /* result */
1529                 NULL,                                   /* pinfo */
1530                 NULL, NULL);                            /* create context */
1531         if (!NT_STATUS_IS_OK(status)) {
1532                 DBG_ERR("Opening [%s] failed: %s\n",
1533                         smb_fname_str_dbg(smb_fname),
1534                         nt_errstr(status));
1535                 return false;
1536         }
1537
1538         status = vfs_streaminfo(handle->conn,
1539                                 fsp,
1540                                 fsp->fsp_name,
1541                                 mem_ctx,
1542                                 num_streams,
1543                                 streams);
1544         if (!NT_STATUS_IS_OK(status)) {
1545                 close_file(NULL, fsp, NORMAL_CLOSE);
1546                 DBG_ERR("streaminfo on [%s] failed: %s\n",
1547                         smb_fname_str_dbg(smb_fname),
1548                         nt_errstr(status));
1549                 return false;
1550         }
1551
1552         status = close_file(NULL, fsp, NORMAL_CLOSE);
1553         if (!NT_STATUS_IS_OK(status)) {
1554                 DBG_ERR("close_file [%s] failed: %s\n",
1555                         smb_fname_str_dbg(smb_fname),
1556                         nt_errstr(status));
1557                 return false;
1558         }
1559
1560         return true;
1561 }
1562
1563 struct ad_collect_state {
1564         bool have_adfile;
1565         size_t adx_data_off;
1566         char *rsrc_data_buf;
1567 };
1568
1569 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1570                                   struct char_mappings **cmaps,
1571                                   struct smb_filename *smb_fname,
1572                                   const struct stream_struct *stream,
1573                                   struct adouble *ad,
1574                                   struct ad_collect_state *state)
1575 {
1576         struct smb_filename *sname = NULL;
1577         files_struct *fsp = NULL;
1578         struct ad_xattr_entry *e = NULL;
1579         char *mapped_name = NULL;
1580         char *p = NULL;
1581         size_t needed_size;
1582         ssize_t nread;
1583         NTSTATUS status;
1584         int ret;
1585         bool ok;
1586
1587         sname = synthetic_smb_fname(ad,
1588                                     smb_fname->base_name,
1589                                     stream->name,
1590                                     NULL,
1591                                     0);
1592         if (sname == NULL) {
1593                 return false;
1594         }
1595
1596         if (is_ntfs_default_stream_smb_fname(sname)) {
1597                 TALLOC_FREE(sname);
1598                 return true;
1599         }
1600
1601         DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1602
1603         ret = SMB_VFS_STAT(handle->conn, sname);
1604         if (ret != 0) {
1605                 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
1606                 ok = false;
1607                 goto out;
1608         }
1609
1610         status = SMB_VFS_CREATE_FILE(
1611                 handle->conn,
1612                 NULL,                           /* req */
1613                 0,                              /* root_dir_fid */
1614                 sname,
1615                 FILE_READ_DATA|DELETE_ACCESS,
1616                 FILE_SHARE_READ,
1617                 FILE_OPEN,
1618                 0,                              /* create_options */
1619                 0,                              /* file_attributes */
1620                 INTERNAL_OPEN_ONLY,             /* oplock_request */
1621                 NULL,                           /* lease */
1622                 0,                              /* allocation_size */
1623                 0,                              /* private_flags */
1624                 NULL,                           /* sd */
1625                 NULL,                           /* ea_list */
1626                 &fsp,
1627                 NULL,                           /* info */
1628                 NULL, NULL);                    /* create context */
1629         if (!NT_STATUS_IS_OK(status)) {
1630                 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1631                         smb_fname_str_dbg(sname));
1632                 ok = false;
1633                 goto out;
1634         }
1635
1636         if (is_afpinfo_stream(stream->name)) {
1637                 char buf[AFP_INFO_SIZE];
1638
1639                 if (stream->size != AFP_INFO_SIZE) {
1640                         DBG_ERR("Bad size [%zd] on [%s]\n",
1641                                 (ssize_t)stream->size,
1642                                 smb_fname_str_dbg(sname));
1643                         ok = false;
1644                         goto out;
1645                 }
1646
1647                 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1648                 if (nread != AFP_INFO_SIZE) {
1649                         DBG_ERR("Bad size [%zd] on [%s]\n",
1650                                 (ssize_t)stream->size,
1651                                 smb_fname_str_dbg(sname));
1652                         ok = false;
1653                         goto out;
1654                 }
1655
1656                 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1657                        buf + AFP_OFF_FinderInfo,
1658                        AFP_FinderSize);
1659
1660                 ok = set_delete_on_close(fsp,
1661                                          true,
1662                                          fsp->conn->session_info->security_token,
1663                                          fsp->conn->session_info->unix_token);
1664                 if (!ok) {
1665                         DBG_ERR("Deleting [%s] failed\n",
1666                                 smb_fname_str_dbg(sname));
1667                         ok = false;
1668                         goto out;
1669                 }
1670                 ok = true;
1671                 goto out;
1672         }
1673
1674         if (is_afpresource_stream(stream->name)) {
1675                 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1676                 if (ad->ad_rsrc_data == NULL) {
1677                         ok = false;
1678                         goto out;
1679                 }
1680
1681                 nread = SMB_VFS_PREAD(fsp,
1682                                       ad->ad_rsrc_data,
1683                                       stream->size,
1684                                       0);
1685                 if (nread != stream->size) {
1686                         DBG_ERR("Bad size [%zd] on [%s]\n",
1687                                 (ssize_t)stream->size,
1688                                 smb_fname_str_dbg(sname));
1689                         ok = false;
1690                         goto out;
1691                 }
1692
1693                 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1694
1695                 if (!state->have_adfile) {
1696                         /*
1697                          * We have a resource *stream* but no AppleDouble
1698                          * sidecar file, this means the share is configured with
1699                          * fruit:resource=stream. So we should delete the
1700                          * resource stream.
1701                          */
1702                         ok = set_delete_on_close(
1703                                 fsp,
1704                                 true,
1705                                 fsp->conn->session_info->security_token,
1706                                 fsp->conn->session_info->unix_token);
1707                         if (!ok) {
1708                                 DBG_ERR("Deleting [%s] failed\n",
1709                                         smb_fname_str_dbg(sname));
1710                                 ok = false;
1711                                 goto out;
1712                         }
1713                 }
1714                 ok = true;
1715                 goto out;
1716         }
1717
1718         ad->adx_entries = talloc_realloc(ad,
1719                                          ad->adx_entries,
1720                                          struct ad_xattr_entry,
1721                                          ad->adx_header.adx_num_attrs + 1);
1722         if (ad->adx_entries == NULL) {
1723                 ok = false;
1724                 goto out;
1725         }
1726
1727         e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1728         *e = (struct ad_xattr_entry) {
1729                 .adx_length = stream->size,
1730         };
1731         e->adx_name = talloc_strdup(ad, stream->name + 1);
1732         if (e->adx_name == NULL) {
1733                 ok = false;
1734                 goto out;
1735         }
1736         p = strchr(e->adx_name, ':');
1737         if (p != NULL) {
1738                 *p = '\0';
1739         }
1740
1741         status = string_replace_allocate(handle->conn,
1742                                          e->adx_name,
1743                                          cmaps,
1744                                          ad,
1745                                          &mapped_name,
1746                                          vfs_translate_to_unix);
1747         if (!NT_STATUS_IS_OK(status) &&
1748             !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1749         {
1750                 DBG_ERR("string_replace_allocate failed\n");
1751                 ok = false;
1752                 goto out;
1753         }
1754
1755         e->adx_name = mapped_name;
1756         e->adx_namelen = strlen(e->adx_name) + 1,
1757
1758         DBG_DEBUG("%u: name (%s) size (%zu)\n",
1759                   ad->adx_header.adx_num_attrs,
1760                   e->adx_name,
1761                   (size_t)e->adx_length);
1762
1763         ad->adx_header.adx_num_attrs++;
1764
1765         needed_size = state->adx_data_off + stream->size;
1766         if (needed_size > talloc_get_size(ad->adx_data)) {
1767                 ad->adx_data = talloc_realloc(ad,
1768                                               ad->adx_data,
1769                                               char,
1770                                               needed_size);
1771                 if (ad->adx_data == NULL) {
1772                         ok = false;
1773                         goto out;
1774                 }
1775         }
1776
1777         nread = SMB_VFS_PREAD(fsp,
1778                               ad->adx_data + state->adx_data_off,
1779                               stream->size,
1780                               0);
1781         if (nread != stream->size) {
1782                 DBG_ERR("Bad size [%zd] on [%s]\n",
1783                         (ssize_t)stream->size,
1784                         smb_fname_str_dbg(sname));
1785                 ok = false;
1786                 goto out;
1787         }
1788         state->adx_data_off += nread;
1789
1790         ok = set_delete_on_close(fsp,
1791                                  true,
1792                                  fsp->conn->session_info->security_token,
1793                                  fsp->conn->session_info->unix_token);
1794         if (!ok) {
1795                 DBG_ERR("Deleting [%s] failed\n",
1796                         smb_fname_str_dbg(sname));
1797                 ok = false;
1798                 goto out;
1799         }
1800
1801 out:
1802         TALLOC_FREE(sname);
1803         if (fsp != NULL) {
1804                 status = close_file(NULL, fsp, NORMAL_CLOSE);
1805                 if (!NT_STATUS_IS_OK(status)) {
1806                         DBG_ERR("close_file [%s] failed: %s\n",
1807                                 smb_fname_str_dbg(smb_fname),
1808                                 nt_errstr(status));
1809                         ok = false;
1810                 }
1811         }
1812
1813         return ok;
1814 }
1815
1816 /**
1817  * Convert filesystem metadata to AppleDouble file
1818  **/
1819 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1820                   struct vfs_handle_struct *handle,
1821                   const char *catia_mappings,
1822                   struct smb_filename *smb_fname,
1823                   bool *converted)
1824 {
1825         static struct char_mappings **cmaps = NULL;
1826         TALLOC_CTX *frame = talloc_stackframe();
1827         struct ad_collect_state state;
1828         struct stream_struct *streams = NULL;
1829         struct smb_filename *adpath = NULL;
1830         struct adouble *ad = NULL;
1831         unsigned int num_streams = 0;
1832         size_t to_convert = 0;
1833         bool have_rsrc;
1834         files_struct *fsp = NULL;
1835         size_t i;
1836         NTSTATUS status;
1837         int ret;
1838         bool ok;
1839
1840         *converted = false;
1841
1842         if (cmaps == NULL) {
1843                 const char **mappings = NULL;
1844
1845                 mappings = str_list_make_v3_const(
1846                         frame, catia_mappings, NULL);
1847                 if (mappings == NULL) {
1848                         ok = false;
1849                         goto out;
1850                 }
1851                 cmaps = string_replace_init_map(mem_ctx, mappings);
1852                 TALLOC_FREE(mappings);
1853         }
1854
1855         ok = ad_unconvert_get_streams(handle,
1856                                       smb_fname,
1857                                       frame,
1858                                       &num_streams,
1859                                       &streams);
1860         if (!ok) {
1861                 goto out;
1862         }
1863
1864         for (i = 0; i < num_streams; i++) {
1865                 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
1866                         continue;
1867                 }
1868                 to_convert++;
1869                 if (is_afpresource_stream(streams[i].name)) {
1870                         have_rsrc = true;
1871                 }
1872         }
1873
1874         if (to_convert == 0) {
1875                 ok = true;
1876                 goto out;
1877         }
1878
1879         state = (struct ad_collect_state) {
1880                 .adx_data_off = 0,
1881         };
1882
1883         ret = adouble_path(frame, smb_fname, &adpath);
1884         if (ret != 0) {
1885                 ok = false;
1886                 goto out;
1887         }
1888
1889         ret = SMB_VFS_STAT(handle->conn, adpath);
1890         if (ret == 0) {
1891                 state.have_adfile = true;
1892         } else {
1893                 if (errno != ENOENT) {
1894                         ok = false;
1895                         goto out;
1896                 }
1897                 state.have_adfile = false;
1898         }
1899
1900         if (to_convert == 1 && have_rsrc && state.have_adfile) {
1901                 /*
1902                  * So we have just a single stream, the resource fork stream
1903                  * from an AppleDouble file. Fine, that means there's nothing to
1904                  * convert.
1905                  */
1906                 ok = true;
1907                 goto out;
1908         }
1909
1910         ad = ad_init(frame, ADOUBLE_RSRC);
1911         if (ad == NULL) {
1912                 ok = false;
1913                 goto out;
1914         }
1915
1916         for (i = 0; i < num_streams; i++) {
1917                 ok = ad_collect_one_stream(handle,
1918                                            cmaps,
1919                                            smb_fname,
1920                                            &streams[i],
1921                                            ad,
1922                                            &state);
1923                 if (!ok) {
1924                         goto out;
1925                 }
1926         }
1927
1928         ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
1929         if (!ok) {
1930                 DBG_ERR("Failed to open adfile [%s]\n",
1931                         smb_fname_str_dbg(smb_fname));
1932                 goto out;
1933         }
1934
1935         ret = ad_fset(handle, ad, fsp);
1936         if (ret != 0) {
1937                 ok = false;
1938                 goto out;
1939         }
1940
1941         *converted = true;
1942         ok = true;
1943
1944 out:
1945         if (fsp != NULL) {
1946                 status = close_file(NULL, fsp, NORMAL_CLOSE);
1947                 if (!NT_STATUS_IS_OK(status)) {
1948                         DBG_ERR("close_file [%s] failed: %s\n",
1949                                 smb_fname_str_dbg(smb_fname),
1950                                 nt_errstr(status));
1951                         ok = false;
1952                 }
1953         }
1954         TALLOC_FREE(frame);
1955         return ok;
1956 }
1957
1958 /**
1959  * Read and parse Netatalk AppleDouble metadata xattr
1960  **/
1961 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1962                             struct adouble *ad,
1963                             const struct smb_filename *smb_fname)
1964 {
1965         int      rc = 0;
1966         ssize_t  ealen;
1967         bool     ok;
1968
1969         DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1970
1971         ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1972                                  AFPINFO_EA_NETATALK, ad->ad_data,
1973                                  AD_DATASZ_XATTR);
1974         if (ealen == -1) {
1975                 switch (errno) {
1976                 case ENOATTR:
1977                 case ENOENT:
1978                         if (errno == ENOATTR) {
1979                                 errno = ENOENT;
1980                         }
1981                         rc = -1;
1982                         goto exit;
1983                 default:
1984                         DEBUG(2, ("error reading meta xattr: %s\n",
1985                                   strerror(errno)));
1986                         rc = -1;
1987                         goto exit;
1988                 }
1989         }
1990         if (ealen != AD_DATASZ_XATTR) {
1991                 DEBUG(2, ("bad size %zd\n", ealen));
1992                 errno = EINVAL;
1993                 rc = -1;
1994                 goto exit;
1995         }
1996
1997         /* Now parse entries */
1998         ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1999         if (!ok) {
2000                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2001                 errno = EINVAL;
2002                 rc = -1;
2003                 goto exit;
2004         }
2005
2006         if (!ad_getentryoff(ad, ADEID_FINDERI)
2007             || !ad_getentryoff(ad, ADEID_COMMENT)
2008             || !ad_getentryoff(ad, ADEID_FILEDATESI)
2009             || !ad_getentryoff(ad, ADEID_AFPFILEI)
2010             || !ad_getentryoff(ad, ADEID_PRIVDEV)
2011             || !ad_getentryoff(ad, ADEID_PRIVINO)
2012             || !ad_getentryoff(ad, ADEID_PRIVSYN)
2013             || !ad_getentryoff(ad, ADEID_PRIVID)) {
2014                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2015                 errno = EINVAL;
2016                 rc = -1;
2017                 goto exit;
2018         }
2019
2020 exit:
2021         DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2022                 smb_fname->base_name, rc));
2023
2024         if (rc != 0) {
2025                 ealen = -1;
2026                 if (errno == EINVAL) {
2027                         become_root();
2028                         (void)SMB_VFS_REMOVEXATTR(handle->conn,
2029                                                   smb_fname,
2030                                                   AFPINFO_EA_NETATALK);
2031                         unbecome_root();
2032                         errno = ENOENT;
2033                 }
2034         }
2035         return ealen;
2036 }
2037
2038 static int ad_open_rsrc(vfs_handle_struct *handle,
2039                         const struct smb_filename *smb_fname,
2040                         int flags,
2041                         mode_t mode,
2042                         files_struct **_fsp)
2043 {
2044         int ret;
2045         struct smb_filename *adp_smb_fname = NULL;
2046         files_struct *fsp = NULL;
2047         uint32_t access_mask;
2048         uint32_t share_access;
2049         uint32_t create_disposition;
2050         NTSTATUS status;
2051
2052         ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2053         if (ret != 0) {
2054                 return -1;
2055         }
2056
2057         ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
2058         if (ret != 0) {
2059                 TALLOC_FREE(adp_smb_fname);
2060                 return -1;
2061         }
2062
2063         access_mask = FILE_GENERIC_READ;
2064         share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
2065         create_disposition = FILE_OPEN;
2066
2067         if (flags & O_RDWR) {
2068                 access_mask |= FILE_GENERIC_WRITE;
2069                 share_access &= ~FILE_SHARE_WRITE;
2070         }
2071
2072         status = SMB_VFS_CREATE_FILE(
2073                 handle->conn,                   /* conn */
2074                 NULL,                           /* req */
2075                 0,                              /* root_dir_fid */
2076                 adp_smb_fname,
2077                 access_mask,
2078                 share_access,
2079                 create_disposition,
2080                 0,                              /* create_options */
2081                 0,                              /* file_attributes */
2082                 INTERNAL_OPEN_ONLY,             /* oplock_request */
2083                 NULL,                           /* lease */
2084                 0,                              /* allocation_size */
2085                 0,                              /* private_flags */
2086                 NULL,                           /* sd */
2087                 NULL,                           /* ea_list */
2088                 &fsp,
2089                 NULL,                           /* psbuf */
2090                 NULL, NULL);                    /* create context */
2091         TALLOC_FREE(adp_smb_fname);
2092         if (!NT_STATUS_IS_OK(status)) {
2093                 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2094                 return -1;
2095         }
2096
2097         *_fsp = fsp;
2098         return 0;
2099 }
2100
2101 /*
2102  * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2103  * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2104  * for file IO on the ._ file.
2105  */
2106 static int ad_open(vfs_handle_struct *handle,
2107                    struct adouble *ad,
2108                    files_struct *fsp,
2109                    const struct smb_filename *smb_fname,
2110                    int flags,
2111                    mode_t mode)
2112 {
2113         int ret;
2114
2115         DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2116                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2117
2118         if (ad->ad_type == ADOUBLE_META) {
2119                 return 0;
2120         }
2121
2122         if (fsp != NULL) {
2123                 ad->ad_fsp = fsp;
2124                 ad->ad_opened = false;
2125                 return 0;
2126         }
2127
2128         ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
2129         if (ret != 0) {
2130                 return -1;
2131         }
2132         ad->ad_opened = true;
2133
2134         DBG_DEBUG("Path [%s] type [%s]\n",
2135                   smb_fname->base_name,
2136                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2137
2138         return 0;
2139 }
2140
2141 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2142                                     struct adouble *ad,
2143                                     const struct smb_filename *smb_fname)
2144 {
2145         char *p_ad = NULL;
2146         size_t size;
2147         ssize_t len;
2148         int ret;
2149         bool ok;
2150
2151         ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2152         if (ret != 0) {
2153                 DBG_ERR("fstat [%s] failed: %s\n",
2154                         fsp_str_dbg(ad->ad_fsp), strerror(errno));
2155                 return -1;
2156         }
2157
2158         /*
2159          * AppleDouble file header content and size, two cases:
2160          *
2161          * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
2162          * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
2163          *
2164          * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
2165          */
2166         size = ad->ad_fsp->fsp_name->st.st_ex_size;
2167         if (size > talloc_array_length(ad->ad_data)) {
2168                 if (size > AD_XATTR_MAX_HDR_SIZE) {
2169                         size = AD_XATTR_MAX_HDR_SIZE;
2170                 }
2171                 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
2172                 if (p_ad == NULL) {
2173                         return -1;
2174                 }
2175                 ad->ad_data = p_ad;
2176         }
2177
2178         len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
2179         if (len != talloc_array_length(ad->ad_data)) {
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, ADEID_NUM_DOT_UND, size);
2187         if (!ok) {
2188                 DBG_ERR("invalid AppleDouble resource %s\n",
2189                         smb_fname->base_name);
2190                 errno = EINVAL;
2191                 return -1;
2192         }
2193
2194         if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2195             || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2196             || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
2197                 DBG_ERR("invalid AppleDouble resource %s\n",
2198                         smb_fname->base_name);
2199                 errno = EINVAL;
2200                 return -1;
2201         }
2202
2203         return len;
2204 }
2205
2206 /**
2207  * Read and parse resource fork, either ._ AppleDouble file or xattr
2208  **/
2209 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2210                             struct adouble *ad,
2211                             const struct smb_filename *smb_fname)
2212 {
2213         return ad_read_rsrc_adouble(handle, ad, smb_fname);
2214 }
2215
2216 /**
2217  * Read and unpack an AppleDouble metadata xattr or resource
2218  **/
2219 static ssize_t ad_read(vfs_handle_struct *handle,
2220                        struct adouble *ad,
2221                        const struct smb_filename *smb_fname)
2222 {
2223         switch (ad->ad_type) {
2224         case ADOUBLE_META:
2225                 return ad_read_meta(handle, ad, smb_fname);
2226         case ADOUBLE_RSRC:
2227                 return ad_read_rsrc(handle, ad, smb_fname);
2228         default:
2229                 return -1;
2230         }
2231 }
2232
2233 static int adouble_destructor(struct adouble *ad)
2234 {
2235         NTSTATUS status;
2236
2237         if (!ad->ad_opened) {
2238                 return 0;
2239         }
2240
2241         SMB_ASSERT(ad->ad_fsp != NULL);
2242
2243         status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
2244         if (!NT_STATUS_IS_OK(status)) {
2245                 DBG_ERR("Closing [%s] failed: %s\n",
2246                         fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2247         }
2248
2249         return 0;
2250 }
2251
2252 /**
2253  * Allocate a struct adouble without initialiing it
2254  *
2255  * The struct is either hang of the fsp extension context or if fsp is
2256  * NULL from ctx.
2257  *
2258  * @param[in] ctx        talloc context
2259  * @param[in] handle     vfs handle
2260  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2261  *
2262  * @return               adouble handle
2263  **/
2264 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2265                                 adouble_type_t type)
2266 {
2267         int rc = 0;
2268         size_t adsize = 0;
2269         struct adouble *ad;
2270
2271         switch (type) {
2272         case ADOUBLE_META:
2273                 adsize = AD_DATASZ_XATTR;
2274                 break;
2275         case ADOUBLE_RSRC:
2276                 adsize = AD_DATASZ_DOT_UND;
2277                 break;
2278         default:
2279                 return NULL;
2280         }
2281
2282         ad = talloc_zero(ctx, struct adouble);
2283         if (ad == NULL) {
2284                 rc = -1;
2285                 goto exit;
2286         }
2287
2288         if (adsize) {
2289                 ad->ad_data = talloc_zero_array(ad, char, adsize);
2290                 if (ad->ad_data == NULL) {
2291                         rc = -1;
2292                         goto exit;
2293                 }
2294         }
2295
2296         ad->ad_type = type;
2297         ad->ad_magic = AD_MAGIC;
2298         ad->ad_version = AD_VERSION;
2299
2300         talloc_set_destructor(ad, adouble_destructor);
2301
2302 exit:
2303         if (rc != 0) {
2304                 TALLOC_FREE(ad);
2305         }
2306         return ad;
2307 }
2308
2309 /**
2310  * Allocate and initialize a new struct adouble
2311  *
2312  * @param[in] ctx        talloc context
2313  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2314  *
2315  * @return               adouble handle, initialized
2316  **/
2317 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2318 {
2319         int rc = 0;
2320         const struct ad_entry_order  *eid;
2321         struct adouble *ad = NULL;
2322         time_t t = time(NULL);
2323
2324         switch (type) {
2325         case ADOUBLE_META:
2326                 eid = entry_order_meta_xattr;
2327                 break;
2328         case ADOUBLE_RSRC:
2329                 eid = entry_order_dot_und;
2330                 break;
2331         default:
2332                 return NULL;
2333         }
2334
2335         ad = ad_alloc(ctx, type);
2336         if (ad == NULL) {
2337                 return NULL;
2338         }
2339
2340         while (eid->id) {
2341                 ad->ad_eid[eid->id].ade_off = eid->offset;
2342                 ad->ad_eid[eid->id].ade_len = eid->len;
2343                 eid++;
2344         }
2345
2346         /* put something sane in the date fields */
2347         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2348         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2349         ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2350         ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2351
2352         if (rc != 0) {
2353                 TALLOC_FREE(ad);
2354         }
2355         return ad;
2356 }
2357
2358 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2359                                        vfs_handle_struct *handle,
2360                                        files_struct *fsp,
2361                                        const struct smb_filename *smb_fname,
2362                                        adouble_type_t type)
2363 {
2364         int rc = 0;
2365         ssize_t len;
2366         struct adouble *ad = NULL;
2367         int mode;
2368
2369         if (fsp != NULL) {
2370                 smb_fname = fsp->base_fsp->fsp_name;
2371         }
2372
2373         DEBUG(10, ("ad_get(%s) called for %s\n",
2374                    type == ADOUBLE_META ? "meta" : "rsrc",
2375                    smb_fname->base_name));
2376
2377         ad = ad_alloc(ctx, type);
2378         if (ad == NULL) {
2379                 rc = -1;
2380                 goto exit;
2381         }
2382
2383         /* Try rw first so we can use the fd in ad_convert() */
2384         mode = O_RDWR;
2385
2386         rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2387         if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2388                 mode = O_RDONLY;
2389                 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2390         }
2391         if (rc == -1) {
2392                 DBG_DEBUG("ad_open [%s] error [%s]\n",
2393                           smb_fname->base_name, strerror(errno));
2394                 goto exit;
2395
2396         }
2397
2398         len = ad_read(handle, ad, smb_fname);
2399         if (len == -1) {
2400                 DEBUG(10, ("error reading AppleDouble for %s\n",
2401                         smb_fname->base_name));
2402                 rc = -1;
2403                 goto exit;
2404         }
2405
2406 exit:
2407         DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2408                   type == ADOUBLE_META ? "meta" : "rsrc",
2409                   smb_fname->base_name, rc));
2410
2411         if (rc != 0) {
2412                 TALLOC_FREE(ad);
2413         }
2414         return ad;
2415 }
2416
2417 /**
2418  * Return AppleDouble data for a file
2419  *
2420  * @param[in] ctx      talloc context
2421  * @param[in] handle   vfs handle
2422  * @param[in] smb_fname pathname to file or directory
2423  * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2424  *
2425  * @return             talloced struct adouble or NULL on error
2426  **/
2427 struct adouble *ad_get(TALLOC_CTX *ctx,
2428                               vfs_handle_struct *handle,
2429                               const struct smb_filename *smb_fname,
2430                               adouble_type_t type)
2431 {
2432         return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2433 }
2434
2435 /**
2436  * Return AppleDouble data for a file
2437  *
2438  * @param[in] ctx      talloc context
2439  * @param[in] handle   vfs handle
2440  * @param[in] fsp      fsp to use for IO
2441  * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2442  *
2443  * @return             talloced struct adouble or NULL on error
2444  **/
2445 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2446                         files_struct *fsp, adouble_type_t type)
2447 {
2448         return ad_get_internal(ctx, handle, fsp, NULL, type);
2449 }
2450
2451 /**
2452  * Set AppleDouble metadata on a file or directory
2453  *
2454  * @param[in] ad      adouble handle
2455  *
2456  * @param[in] smb_fname    pathname to file or directory
2457  *
2458  * @return            status code, 0 means success
2459  **/
2460 int ad_set(vfs_handle_struct *handle,
2461            struct adouble *ad,
2462            const struct smb_filename *smb_fname)
2463 {
2464         bool ok;
2465         int ret;
2466
2467         DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2468
2469         if (ad->ad_type != ADOUBLE_META) {
2470                 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2471                         smb_fname->base_name);
2472                 return -1;
2473         }
2474
2475         ok = ad_pack(handle, ad, NULL);
2476         if (!ok) {
2477                 return -1;
2478         }
2479
2480         ret = SMB_VFS_SETXATTR(handle->conn,
2481                                smb_fname,
2482                                AFPINFO_EA_NETATALK,
2483                                ad->ad_data,
2484                                AD_DATASZ_XATTR, 0);
2485
2486         DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2487
2488         return ret;
2489 }
2490
2491 /**
2492  * Set AppleDouble metadata on a file or directory
2493  *
2494  * @param[in] ad      adouble handle
2495  * @param[in] fsp     file handle
2496  *
2497  * @return            status code, 0 means success
2498  **/
2499 int ad_fset(struct vfs_handle_struct *handle,
2500             struct adouble *ad,
2501             files_struct *fsp)
2502 {
2503         int rc = -1;
2504         ssize_t len;
2505         bool ok;
2506
2507         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2508
2509         if ((fsp == NULL)
2510             || (fsp->fh == NULL)
2511             || (fsp->fh->fd == -1))
2512         {
2513                 smb_panic("bad fsp");
2514         }
2515
2516         ok = ad_pack(handle, ad, fsp);
2517         if (!ok) {
2518                 return -1;
2519         }
2520
2521         switch (ad->ad_type) {
2522         case ADOUBLE_META:
2523                 rc = SMB_VFS_NEXT_SETXATTR(handle,
2524                                            fsp->fsp_name,
2525                                            AFPINFO_EA_NETATALK,
2526                                            ad->ad_data,
2527                                            AD_DATASZ_XATTR, 0);
2528                 break;
2529
2530         case ADOUBLE_RSRC:
2531                 len = SMB_VFS_NEXT_PWRITE(handle,
2532                                           fsp,
2533                                           ad->ad_data,
2534                                           ad_getentryoff(ad, ADEID_RFORK),
2535                                           0);
2536                 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2537                         DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2538                         return -1;
2539                 }
2540                 rc = 0;
2541                 break;
2542
2543         default:
2544                 return -1;
2545         }
2546
2547         DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2548
2549         return rc;
2550 }
2551
2552 bool is_adouble_file(const char *path)
2553 {
2554         const char *p = NULL;
2555         int match;
2556
2557         p = strrchr(path, '/');
2558         if (p == NULL) {
2559                 p = path;
2560         } else {
2561                 p++;
2562         }
2563
2564         match = strncmp(p,
2565                         ADOUBLE_NAME_PREFIX,
2566                         strlen(ADOUBLE_NAME_PREFIX));
2567         if (match != 0) {
2568                 return false;
2569         }
2570         return true;
2571 }
2572
2573 /**
2574  * Prepend "._" to a basename
2575  * Return a new struct smb_filename with stream_name == NULL.
2576  **/
2577 int adouble_path(TALLOC_CTX *ctx,
2578                  const struct smb_filename *smb_fname_in,
2579                  struct smb_filename **pp_smb_fname_out)
2580 {
2581         char *parent;
2582         const char *base;
2583         struct smb_filename *smb_fname = cp_smb_filename(ctx,
2584                                                 smb_fname_in);
2585
2586         if (smb_fname == NULL) {
2587                 return -1;
2588         }
2589
2590         /* We need streamname to be NULL */
2591         TALLOC_FREE(smb_fname->stream_name);
2592
2593         /* And we're replacing base_name. */
2594         TALLOC_FREE(smb_fname->base_name);
2595
2596         SET_STAT_INVALID(smb_fname->st);
2597
2598         if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2599                                 &parent, &base)) {
2600                 TALLOC_FREE(smb_fname);
2601                 return -1;
2602         }
2603
2604         smb_fname->base_name = talloc_asprintf(smb_fname,
2605                                         "%s/._%s", parent, base);
2606         if (smb_fname->base_name == NULL) {
2607                 TALLOC_FREE(smb_fname);
2608                 return -1;
2609         }
2610
2611         *pp_smb_fname_out = smb_fname;
2612
2613         return 0;
2614 }
2615
2616 /**
2617  * Allocate and initialize an AfpInfo struct
2618  **/
2619 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2620 {
2621         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2622         if (ai == NULL) {
2623                 return NULL;
2624         }
2625         ai->afpi_Signature = AFP_Signature;
2626         ai->afpi_Version = AFP_Version;
2627         ai->afpi_BackupTime = AD_DATE_START;
2628         return ai;
2629 }
2630
2631 /**
2632  * Pack an AfpInfo struct into a buffer
2633  *
2634  * Buffer size must be at least AFP_INFO_SIZE
2635  * Returns size of packed buffer
2636  **/
2637 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2638 {
2639         memset(buf, 0, AFP_INFO_SIZE);
2640
2641         RSIVAL(buf, 0, ai->afpi_Signature);
2642         RSIVAL(buf, 4, ai->afpi_Version);
2643         RSIVAL(buf, 12, ai->afpi_BackupTime);
2644         memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2645
2646         return AFP_INFO_SIZE;
2647 }
2648
2649 /**
2650  * Unpack a buffer into a AfpInfo structure
2651  *
2652  * Buffer size must be at least AFP_INFO_SIZE
2653  * Returns allocated AfpInfo struct
2654  **/
2655 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2656 {
2657         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2658         if (ai == NULL) {
2659                 return NULL;
2660         }
2661
2662         ai->afpi_Signature = RIVAL(data, 0);
2663         ai->afpi_Version = RIVAL(data, 4);
2664         ai->afpi_BackupTime = RIVAL(data, 12);
2665         memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2666                sizeof(ai->afpi_FinderInfo));
2667
2668         if (ai->afpi_Signature != AFP_Signature
2669             || ai->afpi_Version != AFP_Version) {
2670                 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2671                 TALLOC_FREE(ai);
2672         }
2673
2674         return ai;
2675 }