s3: libsmb: Cleanup in get_dc_list()
[Samba.git] / source3 / lib / adouble.c
blob42b2e808d66743a92b7c0028b88e65e4d09012a4
1 /*
2 * Samba AppleDouble helpers
4 * Copyright (C) Ralph Boehme, 2019
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
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"
31 "._" AppleDouble Header File Layout:
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
60 '----> RESOURCE FORK
61 ... Variable Sized Data
62 ...
65 /* Number of actually used entries */
66 #define ADEID_NUM_XATTR 8
67 #define ADEID_NUM_DOT_UND 2
68 #define ADEID_NUM_RSRC_XATTR 1
70 /* Sizes of relevant entry bits */
71 #define ADEDLEN_MAGIC 4
72 #define ADEDLEN_VERSION 4
73 #define ADEDLEN_FILLER 16
74 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
75 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
76 #define ADEDLEN_NENTRIES 2
77 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
78 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
79 #define AD_ENTRY_LEN_EID 4
80 #define AD_ENTRY_LEN_OFF 4
81 #define AD_ENTRY_LEN_LEN 4
82 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
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)
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)
101 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
102 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
103 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
105 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
106 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
107 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
108 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
109 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
110 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
112 #if AD_DATASZ_XATTR != 402
113 #error bad size for AD_DATASZ_XATTR
114 #endif
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
123 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
124 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
125 #define AD_XATTR_HDR_SIZE 36
126 #define AD_XATTR_MAX_HDR_SIZE 65536
127 #define ADX_ENTRY_FIXED_SIZE (4+4+2+1)
130 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131 * representation as well as the on-disk format.
133 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
134 * the length of the FinderInfo entry is larger then 32 bytes. It is then
135 * preceeded with 2 bytes padding.
137 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
140 struct ad_xattr_header {
141 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
142 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
143 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
144 uint32_t adx_data_start; /* file offset to attribute data area */
145 uint32_t adx_data_length; /* length of attribute data area */
146 uint32_t adx_reserved[3];
147 uint16_t adx_flags;
148 uint16_t adx_num_attrs;
151 /* On-disk entries are aligned on 4 byte boundaries */
152 struct ad_xattr_entry {
153 uint32_t adx_offset; /* file offset to data */
154 uint32_t adx_length; /* size of attribute data */
155 uint16_t adx_flags;
156 uint8_t adx_namelen; /* included the NULL terminator */
157 char *adx_name; /* NULL-terminated UTF-8 name */
160 struct ad_entry {
161 size_t ade_off;
162 size_t ade_len;
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;
180 struct ad_entry_order {
181 uint32_t id, offset, len;
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}
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}
206 /* Conversion from enumerated id to on-disk AppleDouble id */
207 #define AD_EID_DISK(a) (set_eid[a])
208 static const uint32_t set_eid[] = {
209 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
210 AD_DEV, AD_INO, AD_SYN, AD_ID
213 static char empty_resourcefork[] = {
214 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
216 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
217 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
218 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
219 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
220 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
221 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
252 size_t ad_getentrylen(const struct adouble *ad, int eid)
254 return ad->ad_eid[eid].ade_len;
257 size_t ad_getentryoff(const struct adouble *ad, int eid)
259 return ad->ad_eid[eid].ade_off;
262 size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
264 return ad->ad_eid[eid].ade_len = len;
267 size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
269 return ad->ad_eid[eid].ade_off = off;
273 * Return a pointer to an AppleDouble entry
275 * Returns NULL if the entry is not present
277 char *ad_get_entry(const struct adouble *ad, int eid)
279 off_t off = ad_getentryoff(ad, eid);
280 size_t len = ad_getentrylen(ad, eid);
282 if (off == 0 || len == 0) {
283 return NULL;
286 return ad->ad_data + off;
290 * Get a date
292 int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
294 bool xlate = (dateoff & AD_DATE_UNIX);
295 char *p = NULL;
297 dateoff &= AD_DATE_MASK;
298 p = ad_get_entry(ad, ADEID_FILEDATESI);
299 if (p == NULL) {
300 return -1;
303 if (dateoff > AD_DATE_ACCESS) {
304 return -1;
307 memcpy(date, p + dateoff, sizeof(uint32_t));
309 if (xlate) {
310 *date = AD_DATE_TO_UNIX(*date);
312 return 0;
316 * Set a date
318 int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
320 bool xlate = (dateoff & AD_DATE_UNIX);
321 char *p = NULL;
323 p = ad_get_entry(ad, ADEID_FILEDATESI);
324 if (p == NULL) {
325 return -1;
328 dateoff &= AD_DATE_MASK;
329 if (xlate) {
330 date = AD_DATE_FROM_UNIX(date);
333 if (dateoff > AD_DATE_ACCESS) {
334 return -1;
337 memcpy(p + dateoff, &date, sizeof(date));
339 return 0;
344 * Map on-disk AppleDouble id to enumerated id
346 static uint32_t get_eid(uint32_t eid)
348 if (eid <= 15) {
349 return eid;
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;
365 return 0;
369 * Move resourcefork data in an AppleDouble file
371 * This is supposed to make room in an AppleDouble file by moving the
372 * resourcefork data behind the space required for packing additional xattr data
373 * in the extended FinderInfo entry.
375 * When we're called we're expecting an AppleDouble file with just two entries
376 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
377 * offset of ADEDOFF_RFORK_DOT_UND.
379 static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
380 struct adouble *ad,
381 files_struct *fsp)
383 size_t reso_len;
384 size_t reso_off;
385 size_t n;
386 bool ok;
388 reso_len = ad_getentrylen(ad, ADEID_RFORK);
389 reso_off = ad_getentryoff(ad, ADEID_RFORK);
391 if (reso_len == 0) {
392 return true;
395 if (ad->ad_rsrc_data == NULL) {
397 * This buffer is already set when converting a resourcefork
398 * stream from vfs_streams_depot backend via ad_unconvert(). It
399 * is NULL with vfs_streams_xattr where the resourcefork stream
400 * is stored in an AppleDouble sidecar file vy vfs_fruit.
402 ad->ad_rsrc_data = talloc_size(ad, reso_len);
403 if (ad->ad_rsrc_data == NULL) {
404 return false;
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;
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;
432 ok = true;
433 out:
434 return ok;
437 static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
438 struct adouble *ad,
439 files_struct *fsp)
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;
448 if (ad->adx_entries == NULL) {
449 /* No xattrs, nothing to pack */
450 return true;
453 if (fsp == NULL) {
454 DBG_ERR("fsp unexpectedly NULL\n");
455 return false;
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;
467 memset(ad->ad_data + oldsize,
469 AD_XATTR_MAX_HDR_SIZE - oldsize);
473 * First, let's calculate the start of the xattr data area which will be
474 * after the xattr header + header entries.
477 data_off = ad_getentryoff(ad, ADEID_FINDERI);
478 data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
479 /* 2 bytes padding */
480 data_off += 2;
482 for (i = 0; i < h->adx_num_attrs; i++) {
483 struct ad_xattr_entry *e = &ad->adx_entries[i];
485 /* Align on 4 byte boundary */
486 data_off = (data_off + 3) & ~3;
488 data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
489 if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
490 return false;
494 off = ad_getentryoff(ad, ADEID_FINDERI);
495 off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
496 /* 2 bytes padding */
497 off += 2;
499 for (i = 0; i < h->adx_num_attrs; i++) {
500 struct ad_xattr_entry *e = &ad->adx_entries[i];
502 /* Align on 4 byte boundary */
503 off = (off + 3) & ~3;
505 e->adx_offset = data_off;
506 data_off += e->adx_length;
508 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
509 "adx_data_off [%zu]\n",
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);
517 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
518 return false;
520 RSIVAL(ad->ad_data, off, e->adx_offset);
521 off += 4;
523 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
524 return false;
526 RSIVAL(ad->ad_data, off, e->adx_length);
527 off += 4;
529 if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
530 return false;
532 RSSVAL(ad->ad_data, off, e->adx_flags);
533 off += 2;
535 if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
536 return false;
538 SCVAL(ad->ad_data, off, e->adx_namelen);
539 off += 1;
541 if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
542 return false;
544 memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
545 off += e->adx_namelen;
548 h->adx_data_start = off;
549 h->adx_data_length = talloc_get_size(ad->adx_data);
550 h->adx_total_size = h->adx_data_start + h->adx_data_length;
552 if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
553 ad->ad_data = talloc_realloc(ad,
554 ad->ad_data,
555 char,
556 h->adx_total_size);
557 if (ad->ad_data == NULL) {
558 return false;
562 memcpy(ad->ad_data + h->adx_data_start,
563 ad->adx_data,
564 h->adx_data_length);
566 ad_setentrylen(ad,
567 ADEID_FINDERI,
568 h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
570 ad_setentryoff(ad,
571 ADEID_RFORK,
572 ad_getentryoff(ad, ADEID_FINDERI) +
573 ad_getentrylen(ad, ADEID_FINDERI));
575 memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
578 * Rewind, then update the header fields.
581 off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
582 /* 2 bytes padding */
583 off += 2;
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;
596 /* adx_reserved and adx_flags */
597 memset(ad->ad_data + off, 0, 3 * 4 + 2);
598 off += 3 * 4 + 2;
600 RSSVAL(ad->ad_data, off, h->adx_num_attrs);
601 off += 2;
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;
610 return true;
614 * Pack AppleDouble structure into data buffer
616 static bool ad_pack(struct vfs_handle_struct *handle,
617 struct adouble *ad,
618 files_struct *fsp)
620 uint32_t eid;
621 uint16_t nent;
622 uint32_t bufsize;
623 uint32_t offset = 0;
624 bool ok;
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;
632 if (offset + ADEDLEN_MAGIC < offset ||
633 offset + ADEDLEN_MAGIC >= bufsize) {
634 return false;
636 RSIVAL(ad->ad_data, offset, ad->ad_magic);
637 offset += ADEDLEN_MAGIC;
639 if (offset + ADEDLEN_VERSION < offset ||
640 offset + ADEDLEN_VERSION >= bufsize) {
641 return false;
643 RSIVAL(ad->ad_data, offset, ad->ad_version);
644 offset += ADEDLEN_VERSION;
646 if (offset + ADEDLEN_FILLER < offset ||
647 offset + ADEDLEN_FILLER >= bufsize) {
648 return false;
650 if (ad->ad_type == ADOUBLE_RSRC) {
651 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
653 offset += ADEDLEN_FILLER;
655 if (offset + ADEDLEN_NENTRIES < offset ||
656 offset + ADEDLEN_NENTRIES >= bufsize) {
657 return false;
659 offset += ADEDLEN_NENTRIES;
661 ok = ad_pack_xattrs(handle, ad, fsp);
662 if (!ok) {
663 return false;
666 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
667 if (ad->ad_eid[eid].ade_off == 0) {
669 * ade_off is also used as indicator whether a
670 * specific entry is used or not
672 continue;
675 if (offset + AD_ENTRY_LEN_EID < offset ||
676 offset + AD_ENTRY_LEN_EID >= bufsize) {
677 return false;
679 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
680 offset += AD_ENTRY_LEN_EID;
682 if (offset + AD_ENTRY_LEN_OFF < offset ||
683 offset + AD_ENTRY_LEN_OFF >= bufsize) {
684 return false;
686 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
687 offset += AD_ENTRY_LEN_OFF;
689 if (offset + AD_ENTRY_LEN_LEN < offset ||
690 offset + AD_ENTRY_LEN_LEN >= bufsize) {
691 return false;
693 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
694 offset += AD_ENTRY_LEN_LEN;
696 nent++;
699 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
700 return false;
702 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
704 return true;
707 static bool ad_unpack_xattrs(struct adouble *ad)
709 struct ad_xattr_header *h = &ad->adx_header;
710 const char *p = ad->ad_data;
711 uint32_t hoff;
712 uint32_t i;
714 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
715 return true;
718 /* 2 bytes padding */
719 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
721 h->adx_magic = RIVAL(p, hoff + 0);
722 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
723 h->adx_total_size = RIVAL(p, hoff + 8);
724 h->adx_data_start = RIVAL(p, hoff + 12);
725 h->adx_data_length = RIVAL(p, hoff + 16);
726 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
727 h->adx_num_attrs = RSVAL(p, hoff + 34);
729 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
730 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
731 return false;
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;
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;
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;
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;
752 if ((h->adx_data_start + h->adx_data_length) >
753 ad->adx_header.adx_total_size)
755 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
756 return false;
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;
764 if (h->adx_num_attrs == 0) {
765 return true;
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;
774 hoff += AD_XATTR_HDR_SIZE;
776 for (i = 0; i < h->adx_num_attrs; i++) {
777 struct ad_xattr_entry *e = &ad->adx_entries[i];
779 hoff = (hoff + 3) & ~3;
781 e->adx_offset = RIVAL(p, hoff + 0);
782 e->adx_length = RIVAL(p, hoff + 4);
783 e->adx_flags = RSVAL(p, hoff + 8);
784 e->adx_namelen = *(p + hoff + 10);
786 if (e->adx_offset >= ad->adx_header.adx_total_size) {
787 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
788 e->adx_offset);
789 return false;
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;
798 if ((e->adx_offset + e->adx_length) >
799 ad->adx_header.adx_total_size)
801 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
802 e->adx_length);
803 return false;
806 if (e->adx_namelen == 0) {
807 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
808 e->adx_namelen);
809 return false;
811 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
812 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
813 e->adx_namelen);
814 return false;
816 if ((hoff + 11 + e->adx_namelen) >
817 ad->adx_header.adx_data_start)
819 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
820 e->adx_namelen);
821 return false;
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;
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);
836 hoff += 11 + e->adx_namelen;
839 return true;
843 * Unpack an AppleDouble blob into a struct adoble
845 static bool ad_unpack(struct adouble *ad, const size_t nentries,
846 size_t filesize)
848 size_t bufsize = talloc_get_size(ad->ad_data);
849 size_t adentries, i;
850 uint32_t eid, len, off;
851 bool ok;
854 * The size of the buffer ad->ad_data is checked when read, so
855 * we wouldn't have to check our own offsets, a few extra
856 * checks won't hurt though. We have to check the offsets we
857 * read from the buffer anyway.
860 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
861 DEBUG(1, ("bad size\n"));
862 return false;
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;
872 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
874 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
875 if (adentries != nentries) {
876 DEBUG(1, ("invalid number of entries: %zu\n",
877 adentries));
878 return false;
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);
888 if (!eid || eid >= ADEID_MAX) {
889 DEBUG(1, ("bogus eid %d\n", eid));
890 return false;
894 * All entries other than the resource fork are
895 * expected to be read into the ad_data buffer, so
896 * ensure the specified offset is within that bound
898 if ((off > bufsize) && (eid != ADEID_RFORK)) {
899 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
900 eid, off, len));
901 return false;
905 * All entries besides FinderInfo and resource fork
906 * must fit into the buffer. FinderInfo is special as
907 * it may be larger then the default 32 bytes (if it
908 * contains marshalled xattrs), but we will fixup that
909 * in ad_convert(). And the resource fork is never
910 * accessed directly by the ad_data buf (also see
911 * comment above) anyway.
913 if ((eid != ADEID_RFORK) &&
914 (eid != ADEID_FINDERI) &&
915 ((off + len) > bufsize)) {
916 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
917 eid, off, len));
918 return false;
922 * That would be obviously broken
924 if (off > filesize) {
925 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
926 eid, off, len));
927 return false;
931 * Check for any entry that has its end beyond the
932 * filesize.
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;
941 if (off + len > filesize) {
943 * If this is the resource fork entry, we fix
944 * up the length, for any other entry we bail
945 * out.
947 if (eid != ADEID_RFORK) {
948 DEBUG(1, ("bogus eid %d: off: %" PRIu32
949 ", len: %" PRIu32 "\n",
950 eid, off, len));
951 return false;
955 * Fixup the resource fork entry by limiting
956 * the size to entryoffset - filesize.
958 len = filesize - off;
959 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
960 ", len: %" PRIu32 "\n", off, len));
963 ad->ad_eid[eid].ade_off = off;
964 ad->ad_eid[eid].ade_len = len;
967 ok = ad_unpack_xattrs(ad);
968 if (!ok) {
969 return false;
972 return true;
975 static bool ad_convert_move_reso(vfs_handle_struct *handle,
976 struct adouble *ad,
977 const struct smb_filename *smb_fname)
979 char *buf = NULL;
980 size_t rforklen;
981 size_t rforkoff;
982 ssize_t n;
983 int ret;
985 rforklen = ad_getentrylen(ad, ADEID_RFORK);
986 if (rforklen == 0) {
987 return true;
990 buf = talloc_size(ad, rforklen);
991 if (buf == NULL) {
993 * This allocates a buffer for reading the resource fork data in
994 * one big swoop. Resource forks won't be larger then, say, 64
995 * MB, I swear, so just doing the allocation with the talloc
996 * limit as safeguard seems safe.
998 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
999 rforklen);
1000 return false;
1003 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1005 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1006 if (n != rforklen) {
1007 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1008 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1009 return false;
1012 rforkoff = ADEDOFF_RFORK_DOT_UND;
1014 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1015 if (n != rforklen) {
1016 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1017 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1018 return false;
1021 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
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;
1029 return true;
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)
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;
1045 *converted_xattr = false;
1047 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1048 return true;
1051 if (string_replace_cmaps == NULL) {
1052 const char **mappings = NULL;
1054 mappings = str_list_make_v3_const(
1055 talloc_tos(), catia_mappings, NULL);
1056 if (mappings == NULL) {
1057 return false;
1059 string_replace_cmaps = string_replace_init_map(
1060 handle->conn->sconn, mappings);
1061 TALLOC_FREE(mappings);
1064 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1065 struct ad_xattr_entry *e = &ad->adx_entries[i];
1066 char *mapped_name = NULL;
1067 char *tmp = NULL;
1068 struct smb_filename *stream_name = NULL;
1069 files_struct *fsp = NULL;
1070 ssize_t nwritten;
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))
1081 DBG_ERR("string_replace_allocate failed\n");
1082 ok = false;
1083 goto fail;
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;
1094 stream_name = synthetic_smb_fname(talloc_tos(),
1095 smb_fname->base_name,
1096 mapped_name,
1097 NULL,
1098 smb_fname->twrp,
1099 smb_fname->flags);
1100 TALLOC_FREE(mapped_name);
1101 if (stream_name == NULL) {
1102 DBG_ERR("synthetic_smb_fname failed\n");
1103 ok = false;
1104 goto fail;
1107 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1109 status = SMB_VFS_CREATE_FILE(
1110 handle->conn, /* conn */
1111 NULL, /* req */
1112 &handle->conn->cwd_fsp, /* dirfsp */
1113 stream_name, /* fname */
1114 FILE_GENERIC_WRITE, /* access_mask */
1115 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1116 FILE_OPEN_IF, /* create_disposition */
1117 0, /* create_options */
1118 0, /* file_attributes */
1119 INTERNAL_OPEN_ONLY, /* oplock_request */
1120 NULL, /* lease */
1121 0, /* allocation_size */
1122 0, /* private_flags */
1123 NULL, /* sd */
1124 NULL, /* ea_list */
1125 &fsp, /* result */
1126 NULL, /* psbuf */
1127 NULL, NULL); /* create context */
1128 TALLOC_FREE(stream_name);
1129 if (!NT_STATUS_IS_OK(status)) {
1130 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1131 ok = false;
1132 goto fail;
1135 nwritten = SMB_VFS_PWRITE(fsp,
1136 ad->ad_data + e->adx_offset,
1137 e->adx_length,
1139 if (nwritten == -1) {
1140 DBG_ERR("SMB_VFS_PWRITE failed\n");
1141 saved_errno = errno;
1142 close_file(NULL, fsp, ERROR_CLOSE);
1143 errno = saved_errno;
1144 ok = false;
1145 goto fail;
1148 status = close_file(NULL, fsp, NORMAL_CLOSE);
1149 if (!NT_STATUS_IS_OK(status)) {
1150 ok = false;
1151 goto fail;
1153 fsp = NULL;
1156 ad->adx_header.adx_num_attrs = 0;
1157 TALLOC_FREE(ad->adx_entries);
1159 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1161 rc = ad_fset(handle, ad, ad->ad_fsp);
1162 if (rc != 0) {
1163 DBG_ERR("ad_fset on [%s] failed: %s\n",
1164 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1165 ok = false;
1166 goto fail;
1169 ok = ad_convert_move_reso(handle, ad, smb_fname);
1170 if (!ok) {
1171 goto fail;
1174 *converted_xattr = true;
1175 ok = true;
1177 fail:
1178 return ok;
1181 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1182 struct adouble *ad,
1183 const struct smb_filename *smb_fname)
1185 char *p_ad = NULL;
1186 AfpInfo *ai = NULL;
1187 DATA_BLOB aiblob;
1188 struct smb_filename *stream_name = NULL;
1189 files_struct *fsp = NULL;
1190 size_t size;
1191 ssize_t nwritten;
1192 NTSTATUS status;
1193 int saved_errno = 0;
1194 int cmp;
1196 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1197 if (cmp != 0) {
1198 return true;
1201 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1202 if (p_ad == NULL) {
1203 return false;
1206 ai = afpinfo_new(talloc_tos());
1207 if (ai == NULL) {
1208 return false;
1211 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1213 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1214 if (aiblob.data == NULL) {
1215 TALLOC_FREE(ai);
1216 return false;
1219 size = afpinfo_pack(ai, (char *)aiblob.data);
1220 TALLOC_FREE(ai);
1221 if (size != AFP_INFO_SIZE) {
1222 return false;
1225 stream_name = synthetic_smb_fname(talloc_tos(),
1226 smb_fname->base_name,
1227 AFPINFO_STREAM,
1228 NULL,
1229 smb_fname->twrp,
1230 smb_fname->flags);
1231 if (stream_name == NULL) {
1232 data_blob_free(&aiblob);
1233 DBG_ERR("synthetic_smb_fname failed\n");
1234 return false;
1237 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1239 status = SMB_VFS_CREATE_FILE(
1240 handle->conn, /* conn */
1241 NULL, /* req */
1242 &handle->conn->cwd_fsp, /* dirfsp */
1243 stream_name, /* fname */
1244 FILE_GENERIC_WRITE, /* access_mask */
1245 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1246 FILE_OPEN_IF, /* create_disposition */
1247 0, /* create_options */
1248 0, /* file_attributes */
1249 INTERNAL_OPEN_ONLY, /* oplock_request */
1250 NULL, /* lease */
1251 0, /* allocation_size */
1252 0, /* private_flags */
1253 NULL, /* sd */
1254 NULL, /* ea_list */
1255 &fsp, /* result */
1256 NULL, /* psbuf */
1257 NULL, NULL); /* create context */
1258 TALLOC_FREE(stream_name);
1259 if (!NT_STATUS_IS_OK(status)) {
1260 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1261 return false;
1264 nwritten = SMB_VFS_PWRITE(fsp,
1265 aiblob.data,
1266 aiblob.length,
1268 if (nwritten == -1) {
1269 DBG_ERR("SMB_VFS_PWRITE failed\n");
1270 saved_errno = errno;
1271 close_file(NULL, fsp, ERROR_CLOSE);
1272 errno = saved_errno;
1273 return false;
1276 status = close_file(NULL, fsp, NORMAL_CLOSE);
1277 if (!NT_STATUS_IS_OK(status)) {
1278 return false;
1280 fsp = NULL;
1282 return true;
1285 static bool ad_convert_truncate(vfs_handle_struct *handle,
1286 struct adouble *ad,
1287 const struct smb_filename *smb_fname)
1289 int rc;
1290 off_t newlen;
1292 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1294 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1295 if (rc != 0) {
1296 return false;
1299 return true;
1302 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1303 struct adouble *ad,
1304 uint32_t flags,
1305 bool *blank)
1307 size_t rforklen = sizeof(empty_resourcefork);
1308 char buf[rforklen];
1309 ssize_t nread;
1310 int cmp;
1311 int rc;
1313 *blank = false;
1315 if (!(flags & AD_CONV_WIPE_BLANK)) {
1316 return true;
1319 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1320 return true;
1323 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1324 if (nread != rforklen) {
1325 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1326 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1327 return false;
1330 cmp = memcmp(buf, empty_resourcefork, rforklen);
1331 if (cmp != 0) {
1332 return true;
1335 ad_setentrylen(ad, ADEID_RFORK, 0);
1337 rc = ad_fset(handle, ad, ad->ad_fsp);
1338 if (rc != 0) {
1339 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1340 return false;
1343 *blank = true;
1344 return true;
1347 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1348 struct adouble *ad,
1349 struct files_struct *dirfsp,
1350 const struct smb_filename *smb_fname,
1351 uint32_t flags)
1353 struct smb_filename *ad_name = NULL;
1354 int rc;
1356 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1357 return true;
1360 if (!(flags & AD_CONV_DELETE)) {
1361 return true;
1364 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1365 if (rc != 0) {
1366 return false;
1369 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1370 dirfsp,
1371 ad_name,
1373 if (rc != 0) {
1374 DBG_ERR("Unlinking [%s] failed: %s\n",
1375 smb_fname_str_dbg(ad_name), strerror(errno));
1376 TALLOC_FREE(ad_name);
1377 return false;
1380 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1381 TALLOC_FREE(ad_name);
1383 return true;
1387 * Convert from Apple's ._ file to Netatalk
1389 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1390 * bytes containing packed xattrs.
1392 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1393 * otherwise
1395 int ad_convert(struct vfs_handle_struct *handle,
1396 struct files_struct *dirfsp,
1397 const struct smb_filename *smb_fname,
1398 const char *catia_mappings,
1399 uint32_t flags)
1401 struct adouble *ad = NULL;
1402 bool ok;
1403 bool converted_xattr = false;
1404 bool blank;
1405 int ret;
1407 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1408 if (ad == NULL) {
1409 return 0;
1412 ok = ad_convert_xattr(handle,
1414 smb_fname,
1415 catia_mappings,
1416 &converted_xattr);
1417 if (!ok) {
1418 ret = -1;
1419 goto done;
1422 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1423 if (!ok) {
1424 ret = -1;
1425 goto done;
1428 if (converted_xattr || blank) {
1429 ok = ad_convert_truncate(handle, ad, smb_fname);
1430 if (!ok) {
1431 ret = -1;
1432 goto done;
1436 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1437 if (!ok) {
1438 DBG_ERR("Failed to convert [%s]\n",
1439 smb_fname_str_dbg(smb_fname));
1440 ret = -1;
1441 goto done;
1444 ok = ad_convert_delete_adfile(handle,
1446 dirfsp,
1447 smb_fname,
1448 flags);
1449 if (!ok) {
1450 ret = -1;
1451 goto done;
1454 ret = 0;
1455 done:
1456 TALLOC_FREE(ad);
1457 return ret;
1460 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1461 struct vfs_handle_struct *handle,
1462 struct smb_filename *smb_fname,
1463 struct smb_filename *adpath,
1464 files_struct **_fsp)
1466 files_struct *fsp = NULL;
1467 NTSTATUS status;
1468 int ret;
1470 status = SMB_VFS_CREATE_FILE(
1471 handle->conn,
1472 NULL, /* req */
1473 &handle->conn->cwd_fsp, /* dirfsp */
1474 adpath,
1475 FILE_READ_DATA|FILE_WRITE_DATA,
1476 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1477 FILE_OPEN_IF,
1478 0, /* create_options */
1479 0, /* file_attributes */
1480 INTERNAL_OPEN_ONLY,
1481 NULL, /* lease */
1482 0, /* allocation_size */
1483 0, /* private_flags */
1484 NULL, /* sd */
1485 NULL, /* ea_list */
1486 &fsp,
1487 NULL, /* info */
1488 NULL, NULL); /* create context */
1489 if (!NT_STATUS_IS_OK(status)) {
1490 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1491 smb_fname_str_dbg(adpath), nt_errstr(status));
1492 return false;
1495 if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1496 fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1498 ret = SMB_VFS_FCHOWN(fsp,
1499 smb_fname->st.st_ex_uid,
1500 smb_fname->st.st_ex_gid);
1501 if (ret != 0) {
1502 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1503 fsp_str_dbg(fsp), nt_errstr(status));
1504 close_file(NULL, fsp, NORMAL_CLOSE);
1505 return false;
1509 *_fsp = fsp;
1510 return true;
1513 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1514 struct smb_filename *smb_fname,
1515 TALLOC_CTX *mem_ctx,
1516 unsigned int *num_streams,
1517 struct stream_struct **streams)
1519 files_struct *fsp = NULL;
1520 NTSTATUS status;
1522 status = SMB_VFS_CREATE_FILE(
1523 handle->conn, /* conn */
1524 NULL, /* req */
1525 &handle->conn->cwd_fsp, /* dirfsp */
1526 smb_fname, /* fname */
1527 FILE_READ_ATTRIBUTES, /* access_mask */
1528 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1529 FILE_SHARE_DELETE),
1530 FILE_OPEN, /* create_disposition*/
1531 0, /* create_options */
1532 0, /* file_attributes */
1533 INTERNAL_OPEN_ONLY, /* oplock_request */
1534 NULL, /* lease */
1535 0, /* allocation_size */
1536 0, /* private_flags */
1537 NULL, /* sd */
1538 NULL, /* ea_list */
1539 &fsp, /* result */
1540 NULL, /* pinfo */
1541 NULL, NULL); /* create context */
1542 if (!NT_STATUS_IS_OK(status)) {
1543 DBG_ERR("Opening [%s] failed: %s\n",
1544 smb_fname_str_dbg(smb_fname),
1545 nt_errstr(status));
1546 return false;
1549 status = vfs_streaminfo(handle->conn,
1550 fsp,
1551 fsp->fsp_name,
1552 mem_ctx,
1553 num_streams,
1554 streams);
1555 if (!NT_STATUS_IS_OK(status)) {
1556 close_file(NULL, fsp, NORMAL_CLOSE);
1557 DBG_ERR("streaminfo on [%s] failed: %s\n",
1558 smb_fname_str_dbg(smb_fname),
1559 nt_errstr(status));
1560 return false;
1563 status = close_file(NULL, fsp, NORMAL_CLOSE);
1564 if (!NT_STATUS_IS_OK(status)) {
1565 DBG_ERR("close_file [%s] failed: %s\n",
1566 smb_fname_str_dbg(smb_fname),
1567 nt_errstr(status));
1568 return false;
1571 return true;
1574 struct ad_collect_state {
1575 bool have_adfile;
1576 size_t adx_data_off;
1577 char *rsrc_data_buf;
1580 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1581 struct char_mappings **cmaps,
1582 struct smb_filename *smb_fname,
1583 const struct stream_struct *stream,
1584 struct adouble *ad,
1585 struct ad_collect_state *state)
1587 struct smb_filename *sname = NULL;
1588 files_struct *fsp = NULL;
1589 struct ad_xattr_entry *e = NULL;
1590 char *mapped_name = NULL;
1591 char *p = NULL;
1592 size_t needed_size;
1593 ssize_t nread;
1594 NTSTATUS status;
1595 int ret;
1596 bool ok;
1598 sname = synthetic_smb_fname(ad,
1599 smb_fname->base_name,
1600 stream->name,
1601 NULL,
1602 smb_fname->twrp,
1604 if (sname == NULL) {
1605 return false;
1608 if (is_ntfs_default_stream_smb_fname(sname)) {
1609 TALLOC_FREE(sname);
1610 return true;
1613 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1615 ret = SMB_VFS_STAT(handle->conn, sname);
1616 if (ret != 0) {
1617 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
1618 ok = false;
1619 goto out;
1622 status = SMB_VFS_CREATE_FILE(
1623 handle->conn,
1624 NULL, /* req */
1625 &handle->conn->cwd_fsp, /* dirfsp */
1626 sname,
1627 FILE_READ_DATA|DELETE_ACCESS,
1628 FILE_SHARE_READ,
1629 FILE_OPEN,
1630 0, /* create_options */
1631 0, /* file_attributes */
1632 INTERNAL_OPEN_ONLY, /* oplock_request */
1633 NULL, /* lease */
1634 0, /* allocation_size */
1635 0, /* private_flags */
1636 NULL, /* sd */
1637 NULL, /* ea_list */
1638 &fsp,
1639 NULL, /* info */
1640 NULL, NULL); /* create context */
1641 if (!NT_STATUS_IS_OK(status)) {
1642 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1643 smb_fname_str_dbg(sname));
1644 ok = false;
1645 goto out;
1648 if (is_afpinfo_stream(stream->name)) {
1649 char buf[AFP_INFO_SIZE];
1651 if (stream->size != AFP_INFO_SIZE) {
1652 DBG_ERR("Bad size [%zd] on [%s]\n",
1653 (ssize_t)stream->size,
1654 smb_fname_str_dbg(sname));
1655 ok = false;
1656 goto out;
1659 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1660 if (nread != AFP_INFO_SIZE) {
1661 DBG_ERR("Bad size [%zd] on [%s]\n",
1662 (ssize_t)stream->size,
1663 smb_fname_str_dbg(sname));
1664 ok = false;
1665 goto out;
1668 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1669 buf + AFP_OFF_FinderInfo,
1670 AFP_FinderSize);
1672 ok = set_delete_on_close(fsp,
1673 true,
1674 fsp->conn->session_info->security_token,
1675 fsp->conn->session_info->unix_token);
1676 if (!ok) {
1677 DBG_ERR("Deleting [%s] failed\n",
1678 smb_fname_str_dbg(sname));
1679 ok = false;
1680 goto out;
1682 ok = true;
1683 goto out;
1686 if (is_afpresource_stream(stream->name)) {
1687 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1688 if (ad->ad_rsrc_data == NULL) {
1689 ok = false;
1690 goto out;
1693 nread = SMB_VFS_PREAD(fsp,
1694 ad->ad_rsrc_data,
1695 stream->size,
1697 if (nread != stream->size) {
1698 DBG_ERR("Bad size [%zd] on [%s]\n",
1699 (ssize_t)stream->size,
1700 smb_fname_str_dbg(sname));
1701 ok = false;
1702 goto out;
1705 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1707 if (!state->have_adfile) {
1709 * We have a resource *stream* but no AppleDouble
1710 * sidecar file, this means the share is configured with
1711 * fruit:resource=stream. So we should delete the
1712 * resource stream.
1714 ok = set_delete_on_close(
1715 fsp,
1716 true,
1717 fsp->conn->session_info->security_token,
1718 fsp->conn->session_info->unix_token);
1719 if (!ok) {
1720 DBG_ERR("Deleting [%s] failed\n",
1721 smb_fname_str_dbg(sname));
1722 ok = false;
1723 goto out;
1726 ok = true;
1727 goto out;
1730 ad->adx_entries = talloc_realloc(ad,
1731 ad->adx_entries,
1732 struct ad_xattr_entry,
1733 ad->adx_header.adx_num_attrs + 1);
1734 if (ad->adx_entries == NULL) {
1735 ok = false;
1736 goto out;
1739 e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1740 *e = (struct ad_xattr_entry) {
1741 .adx_length = stream->size,
1743 e->adx_name = talloc_strdup(ad, stream->name + 1);
1744 if (e->adx_name == NULL) {
1745 ok = false;
1746 goto out;
1748 p = strchr(e->adx_name, ':');
1749 if (p != NULL) {
1750 *p = '\0';
1753 status = string_replace_allocate(handle->conn,
1754 e->adx_name,
1755 cmaps,
1757 &mapped_name,
1758 vfs_translate_to_unix);
1759 if (!NT_STATUS_IS_OK(status) &&
1760 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1762 DBG_ERR("string_replace_allocate failed\n");
1763 ok = false;
1764 goto out;
1767 e->adx_name = mapped_name;
1768 e->adx_namelen = strlen(e->adx_name) + 1,
1770 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1771 ad->adx_header.adx_num_attrs,
1772 e->adx_name,
1773 (size_t)e->adx_length);
1775 ad->adx_header.adx_num_attrs++;
1777 needed_size = state->adx_data_off + stream->size;
1778 if (needed_size > talloc_get_size(ad->adx_data)) {
1779 ad->adx_data = talloc_realloc(ad,
1780 ad->adx_data,
1781 char,
1782 needed_size);
1783 if (ad->adx_data == NULL) {
1784 ok = false;
1785 goto out;
1789 nread = SMB_VFS_PREAD(fsp,
1790 ad->adx_data + state->adx_data_off,
1791 stream->size,
1793 if (nread != stream->size) {
1794 DBG_ERR("Bad size [%zd] on [%s]\n",
1795 (ssize_t)stream->size,
1796 smb_fname_str_dbg(sname));
1797 ok = false;
1798 goto out;
1800 state->adx_data_off += nread;
1802 ok = set_delete_on_close(fsp,
1803 true,
1804 fsp->conn->session_info->security_token,
1805 fsp->conn->session_info->unix_token);
1806 if (!ok) {
1807 DBG_ERR("Deleting [%s] failed\n",
1808 smb_fname_str_dbg(sname));
1809 ok = false;
1810 goto out;
1813 out:
1814 TALLOC_FREE(sname);
1815 if (fsp != NULL) {
1816 status = close_file(NULL, fsp, NORMAL_CLOSE);
1817 if (!NT_STATUS_IS_OK(status)) {
1818 DBG_ERR("close_file [%s] failed: %s\n",
1819 smb_fname_str_dbg(smb_fname),
1820 nt_errstr(status));
1821 ok = false;
1825 return ok;
1829 * Convert filesystem metadata to AppleDouble file
1831 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1832 struct vfs_handle_struct *handle,
1833 const char *catia_mappings,
1834 struct smb_filename *smb_fname,
1835 bool *converted)
1837 static struct char_mappings **cmaps = NULL;
1838 TALLOC_CTX *frame = talloc_stackframe();
1839 struct ad_collect_state state;
1840 struct stream_struct *streams = NULL;
1841 struct smb_filename *adpath = NULL;
1842 struct adouble *ad = NULL;
1843 unsigned int num_streams = 0;
1844 size_t to_convert = 0;
1845 bool have_rsrc = false;
1846 files_struct *fsp = NULL;
1847 size_t i;
1848 NTSTATUS status;
1849 int ret;
1850 bool ok;
1852 *converted = false;
1854 if (cmaps == NULL) {
1855 const char **mappings = NULL;
1857 mappings = str_list_make_v3_const(
1858 frame, catia_mappings, NULL);
1859 if (mappings == NULL) {
1860 ok = false;
1861 goto out;
1863 cmaps = string_replace_init_map(mem_ctx, mappings);
1864 TALLOC_FREE(mappings);
1867 ok = ad_unconvert_get_streams(handle,
1868 smb_fname,
1869 frame,
1870 &num_streams,
1871 &streams);
1872 if (!ok) {
1873 goto out;
1876 for (i = 0; i < num_streams; i++) {
1877 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
1878 continue;
1880 to_convert++;
1881 if (is_afpresource_stream(streams[i].name)) {
1882 have_rsrc = true;
1886 if (to_convert == 0) {
1887 ok = true;
1888 goto out;
1891 state = (struct ad_collect_state) {
1892 .adx_data_off = 0,
1895 ret = adouble_path(frame, smb_fname, &adpath);
1896 if (ret != 0) {
1897 ok = false;
1898 goto out;
1901 ret = SMB_VFS_STAT(handle->conn, adpath);
1902 if (ret == 0) {
1903 state.have_adfile = true;
1904 } else {
1905 if (errno != ENOENT) {
1906 ok = false;
1907 goto out;
1909 state.have_adfile = false;
1912 if (to_convert == 1 && have_rsrc && state.have_adfile) {
1914 * So we have just a single stream, the resource fork stream
1915 * from an AppleDouble file. Fine, that means there's nothing to
1916 * convert.
1918 ok = true;
1919 goto out;
1922 ad = ad_init(frame, ADOUBLE_RSRC);
1923 if (ad == NULL) {
1924 ok = false;
1925 goto out;
1928 for (i = 0; i < num_streams; i++) {
1929 ok = ad_collect_one_stream(handle,
1930 cmaps,
1931 smb_fname,
1932 &streams[i],
1934 &state);
1935 if (!ok) {
1936 goto out;
1940 ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
1941 if (!ok) {
1942 DBG_ERR("Failed to open adfile [%s]\n",
1943 smb_fname_str_dbg(smb_fname));
1944 goto out;
1947 ret = ad_fset(handle, ad, fsp);
1948 if (ret != 0) {
1949 ok = false;
1950 goto out;
1953 *converted = true;
1954 ok = true;
1956 out:
1957 if (fsp != NULL) {
1958 status = close_file(NULL, fsp, NORMAL_CLOSE);
1959 if (!NT_STATUS_IS_OK(status)) {
1960 DBG_ERR("close_file [%s] failed: %s\n",
1961 smb_fname_str_dbg(smb_fname),
1962 nt_errstr(status));
1963 ok = false;
1966 TALLOC_FREE(frame);
1967 return ok;
1971 * Read and parse Netatalk AppleDouble metadata xattr
1973 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1974 struct adouble *ad,
1975 const struct smb_filename *smb_fname)
1977 int rc = 0;
1978 ssize_t ealen;
1979 bool ok;
1981 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1983 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1984 AFPINFO_EA_NETATALK, ad->ad_data,
1985 AD_DATASZ_XATTR);
1986 if (ealen == -1) {
1987 switch (errno) {
1988 case ENOATTR:
1989 case ENOENT:
1990 if (errno == ENOATTR) {
1991 errno = ENOENT;
1993 rc = -1;
1994 goto exit;
1995 default:
1996 DEBUG(2, ("error reading meta xattr: %s\n",
1997 strerror(errno)));
1998 rc = -1;
1999 goto exit;
2002 if (ealen != AD_DATASZ_XATTR) {
2003 DEBUG(2, ("bad size %zd\n", ealen));
2004 errno = EINVAL;
2005 rc = -1;
2006 goto exit;
2009 /* Now parse entries */
2010 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2011 if (!ok) {
2012 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2013 errno = EINVAL;
2014 rc = -1;
2015 goto exit;
2018 if (!ad_getentryoff(ad, ADEID_FINDERI)
2019 || !ad_getentryoff(ad, ADEID_COMMENT)
2020 || !ad_getentryoff(ad, ADEID_FILEDATESI)
2021 || !ad_getentryoff(ad, ADEID_AFPFILEI)
2022 || !ad_getentryoff(ad, ADEID_PRIVDEV)
2023 || !ad_getentryoff(ad, ADEID_PRIVINO)
2024 || !ad_getentryoff(ad, ADEID_PRIVSYN)
2025 || !ad_getentryoff(ad, ADEID_PRIVID)) {
2026 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2027 errno = EINVAL;
2028 rc = -1;
2029 goto exit;
2032 exit:
2033 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2034 smb_fname->base_name, rc));
2036 if (rc != 0) {
2037 ealen = -1;
2038 if (errno == EINVAL) {
2039 become_root();
2040 (void)SMB_VFS_REMOVEXATTR(handle->conn,
2041 smb_fname,
2042 AFPINFO_EA_NETATALK);
2043 unbecome_root();
2044 errno = ENOENT;
2047 return ealen;
2050 static int ad_open_rsrc(vfs_handle_struct *handle,
2051 const struct smb_filename *smb_fname,
2052 int flags,
2053 mode_t mode,
2054 files_struct **_fsp)
2056 int ret;
2057 struct smb_filename *adp_smb_fname = NULL;
2058 files_struct *fsp = NULL;
2059 uint32_t access_mask;
2060 uint32_t share_access;
2061 uint32_t create_disposition;
2062 NTSTATUS status;
2064 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2065 if (ret != 0) {
2066 return -1;
2069 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
2070 if (ret != 0) {
2071 TALLOC_FREE(adp_smb_fname);
2072 return -1;
2075 access_mask = FILE_GENERIC_READ;
2076 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
2077 create_disposition = FILE_OPEN;
2079 if (flags & O_RDWR) {
2080 access_mask |= FILE_GENERIC_WRITE;
2081 share_access &= ~FILE_SHARE_WRITE;
2084 status = SMB_VFS_CREATE_FILE(
2085 handle->conn, /* conn */
2086 NULL, /* req */
2087 &handle->conn->cwd_fsp, /* dirfsp */
2088 adp_smb_fname,
2089 access_mask,
2090 share_access,
2091 create_disposition,
2092 0, /* create_options */
2093 0, /* file_attributes */
2094 INTERNAL_OPEN_ONLY, /* oplock_request */
2095 NULL, /* lease */
2096 0, /* allocation_size */
2097 0, /* private_flags */
2098 NULL, /* sd */
2099 NULL, /* ea_list */
2100 &fsp,
2101 NULL, /* psbuf */
2102 NULL, NULL); /* create context */
2103 TALLOC_FREE(adp_smb_fname);
2104 if (!NT_STATUS_IS_OK(status)) {
2105 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2106 return -1;
2109 *_fsp = fsp;
2110 return 0;
2114 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2115 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2116 * for file IO on the ._ file.
2118 static int ad_open(vfs_handle_struct *handle,
2119 struct adouble *ad,
2120 files_struct *fsp,
2121 const struct smb_filename *smb_fname,
2122 int flags,
2123 mode_t mode)
2125 int ret;
2127 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2128 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2130 if (ad->ad_type == ADOUBLE_META) {
2131 return 0;
2134 if (fsp != NULL) {
2135 ad->ad_fsp = fsp;
2136 ad->ad_opened = false;
2137 return 0;
2140 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
2141 if (ret != 0) {
2142 return -1;
2144 ad->ad_opened = true;
2146 DBG_DEBUG("Path [%s] type [%s]\n",
2147 smb_fname->base_name,
2148 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2150 return 0;
2153 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2154 struct adouble *ad,
2155 const struct smb_filename *smb_fname)
2157 size_t to_read;
2158 ssize_t len;
2159 int ret;
2160 bool ok;
2162 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2163 if (ret != 0) {
2164 DBG_ERR("fstat [%s] failed: %s\n",
2165 fsp_str_dbg(ad->ad_fsp), strerror(errno));
2166 return -1;
2169 to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2170 if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2171 to_read = AD_XATTR_MAX_HDR_SIZE;
2174 len = SMB_VFS_NEXT_PREAD(handle,
2175 ad->ad_fsp,
2176 ad->ad_data,
2177 to_read,
2179 if (len != to_read) {
2180 DBG_NOTICE("%s %s: bad size: %zd\n",
2181 smb_fname->base_name, strerror(errno), len);
2182 return -1;
2185 /* Now parse entries */
2186 ok = ad_unpack(ad,
2187 ADEID_NUM_DOT_UND,
2188 ad->ad_fsp->fsp_name->st.st_ex_size);
2189 if (!ok) {
2190 DBG_ERR("invalid AppleDouble resource %s\n",
2191 smb_fname->base_name);
2192 errno = EINVAL;
2193 return -1;
2196 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2197 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2198 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2200 DBG_ERR("invalid AppleDouble resource %s\n",
2201 smb_fname->base_name);
2202 errno = EINVAL;
2203 return -1;
2206 return len;
2210 * Read and parse resource fork, either ._ AppleDouble file or xattr
2212 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2213 struct adouble *ad,
2214 const struct smb_filename *smb_fname)
2216 return ad_read_rsrc_adouble(handle, ad, smb_fname);
2220 * Read and unpack an AppleDouble metadata xattr or resource
2222 static ssize_t ad_read(vfs_handle_struct *handle,
2223 struct adouble *ad,
2224 const struct smb_filename *smb_fname)
2226 switch (ad->ad_type) {
2227 case ADOUBLE_META:
2228 return ad_read_meta(handle, ad, smb_fname);
2229 case ADOUBLE_RSRC:
2230 return ad_read_rsrc(handle, ad, smb_fname);
2231 default:
2232 return -1;
2236 static int adouble_destructor(struct adouble *ad)
2238 NTSTATUS status;
2240 if (!ad->ad_opened) {
2241 return 0;
2244 SMB_ASSERT(ad->ad_fsp != NULL);
2246 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
2247 if (!NT_STATUS_IS_OK(status)) {
2248 DBG_ERR("Closing [%s] failed: %s\n",
2249 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2252 return 0;
2256 * Allocate a struct adouble without initialiing it
2258 * The struct is either hang of the fsp extension context or if fsp is
2259 * NULL from ctx.
2261 * @param[in] ctx talloc context
2262 * @param[in] handle vfs handle
2263 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2265 * @return adouble handle
2267 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2268 adouble_type_t type)
2270 int rc = 0;
2271 size_t adsize = 0;
2272 struct adouble *ad;
2274 switch (type) {
2275 case ADOUBLE_META:
2276 adsize = AD_DATASZ_XATTR;
2277 break;
2278 case ADOUBLE_RSRC:
2280 * AppleDouble ._ file case, optimize for fewer (but larger)
2281 * IOs. Two cases:
2283 * - without xattrs size of the header is exactly
2284 * AD_DATASZ_DOT_UND (82) bytes
2286 * - with embedded xattrs it can be larger, up to
2287 * AD_XATTR_MAX_HDR_SIZE
2289 * Larger headers are not supported, but this is a reasonable
2290 * limit that is also employed by the macOS client.
2292 * We used the largest possible size to be able to read the full
2293 * header with one IO.
2295 adsize = AD_XATTR_MAX_HDR_SIZE;
2296 break;
2297 default:
2298 return NULL;
2301 ad = talloc_zero(ctx, struct adouble);
2302 if (ad == NULL) {
2303 rc = -1;
2304 goto exit;
2307 if (adsize) {
2308 ad->ad_data = talloc_zero_array(ad, char, adsize);
2309 if (ad->ad_data == NULL) {
2310 rc = -1;
2311 goto exit;
2315 ad->ad_type = type;
2316 ad->ad_magic = AD_MAGIC;
2317 ad->ad_version = AD_VERSION;
2319 talloc_set_destructor(ad, adouble_destructor);
2321 exit:
2322 if (rc != 0) {
2323 TALLOC_FREE(ad);
2325 return ad;
2329 * Allocate and initialize a new struct adouble
2331 * @param[in] ctx talloc context
2332 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2334 * @return adouble handle, initialized
2336 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2338 int rc = 0;
2339 const struct ad_entry_order *eid;
2340 struct adouble *ad = NULL;
2341 time_t t = time(NULL);
2343 switch (type) {
2344 case ADOUBLE_META:
2345 eid = entry_order_meta_xattr;
2346 break;
2347 case ADOUBLE_RSRC:
2348 eid = entry_order_dot_und;
2349 break;
2350 default:
2351 return NULL;
2354 ad = ad_alloc(ctx, type);
2355 if (ad == NULL) {
2356 return NULL;
2359 while (eid->id) {
2360 ad->ad_eid[eid->id].ade_off = eid->offset;
2361 ad->ad_eid[eid->id].ade_len = eid->len;
2362 eid++;
2365 /* put something sane in the date fields */
2366 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2367 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2368 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2369 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2371 if (rc != 0) {
2372 TALLOC_FREE(ad);
2374 return ad;
2377 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2378 vfs_handle_struct *handle,
2379 files_struct *fsp,
2380 const struct smb_filename *smb_fname,
2381 adouble_type_t type)
2383 int rc = 0;
2384 ssize_t len;
2385 struct adouble *ad = NULL;
2386 int mode;
2388 if (fsp != NULL) {
2389 smb_fname = fsp->base_fsp->fsp_name;
2392 DEBUG(10, ("ad_get(%s) called for %s\n",
2393 type == ADOUBLE_META ? "meta" : "rsrc",
2394 smb_fname != NULL ? smb_fname->base_name : "???"));
2396 ad = ad_alloc(ctx, type);
2397 if (ad == NULL) {
2398 rc = -1;
2399 goto exit;
2402 /* Try rw first so we can use the fd in ad_convert() */
2403 mode = O_RDWR;
2405 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2406 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2407 mode = O_RDONLY;
2408 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2410 if (rc == -1) {
2411 DBG_DEBUG("ad_open [%s] error [%s]\n",
2412 smb_fname->base_name, strerror(errno));
2413 goto exit;
2417 len = ad_read(handle, ad, smb_fname);
2418 if (len == -1) {
2419 DEBUG(10, ("error reading AppleDouble for %s\n",
2420 smb_fname->base_name));
2421 rc = -1;
2422 goto exit;
2425 exit:
2426 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2427 type == ADOUBLE_META ? "meta" : "rsrc",
2428 smb_fname->base_name, rc));
2430 if (rc != 0) {
2431 TALLOC_FREE(ad);
2433 return ad;
2437 * Return AppleDouble data for a file
2439 * @param[in] ctx talloc context
2440 * @param[in] handle vfs handle
2441 * @param[in] smb_fname pathname to file or directory
2442 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2444 * @return talloced struct adouble or NULL on error
2446 struct adouble *ad_get(TALLOC_CTX *ctx,
2447 vfs_handle_struct *handle,
2448 const struct smb_filename *smb_fname,
2449 adouble_type_t type)
2451 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2455 * Return AppleDouble data for a file
2457 * @param[in] ctx talloc context
2458 * @param[in] handle vfs handle
2459 * @param[in] fsp fsp to use for IO
2460 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2462 * @return talloced struct adouble or NULL on error
2464 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2465 files_struct *fsp, adouble_type_t type)
2467 return ad_get_internal(ctx, handle, fsp, NULL, type);
2471 * Set AppleDouble metadata on a file or directory
2473 * @param[in] ad adouble handle
2475 * @param[in] smb_fname pathname to file or directory
2477 * @return status code, 0 means success
2479 int ad_set(vfs_handle_struct *handle,
2480 struct adouble *ad,
2481 const struct smb_filename *smb_fname)
2483 bool ok;
2484 int ret;
2486 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2488 if (ad->ad_type != ADOUBLE_META) {
2489 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2490 smb_fname->base_name);
2491 return -1;
2494 ok = ad_pack(handle, ad, NULL);
2495 if (!ok) {
2496 return -1;
2499 ret = SMB_VFS_SETXATTR(handle->conn,
2500 smb_fname,
2501 AFPINFO_EA_NETATALK,
2502 ad->ad_data,
2503 AD_DATASZ_XATTR, 0);
2505 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2507 return ret;
2511 * Set AppleDouble metadata on a file or directory
2513 * @param[in] ad adouble handle
2514 * @param[in] fsp file handle
2516 * @return status code, 0 means success
2518 int ad_fset(struct vfs_handle_struct *handle,
2519 struct adouble *ad,
2520 files_struct *fsp)
2522 int rc = -1;
2523 ssize_t len;
2524 bool ok;
2526 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2528 if ((fsp == NULL)
2529 || (fsp->fh == NULL)
2530 || (fsp->fh->fd == -1))
2532 smb_panic("bad fsp");
2535 ok = ad_pack(handle, ad, fsp);
2536 if (!ok) {
2537 return -1;
2540 switch (ad->ad_type) {
2541 case ADOUBLE_META:
2542 rc = SMB_VFS_NEXT_SETXATTR(handle,
2543 fsp->fsp_name,
2544 AFPINFO_EA_NETATALK,
2545 ad->ad_data,
2546 AD_DATASZ_XATTR, 0);
2547 break;
2549 case ADOUBLE_RSRC:
2550 len = SMB_VFS_NEXT_PWRITE(handle,
2551 fsp,
2552 ad->ad_data,
2553 ad_getentryoff(ad, ADEID_RFORK),
2555 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2556 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2557 return -1;
2559 rc = 0;
2560 break;
2562 default:
2563 return -1;
2566 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2568 return rc;
2571 bool is_adouble_file(const char *path)
2573 const char *p = NULL;
2574 int match;
2576 p = strrchr(path, '/');
2577 if (p == NULL) {
2578 p = path;
2579 } else {
2580 p++;
2583 match = strncmp(p,
2584 ADOUBLE_NAME_PREFIX,
2585 strlen(ADOUBLE_NAME_PREFIX));
2586 if (match != 0) {
2587 return false;
2589 return true;
2593 * Prepend "._" to a basename
2594 * Return a new struct smb_filename with stream_name == NULL.
2596 int adouble_path(TALLOC_CTX *ctx,
2597 const struct smb_filename *smb_fname_in,
2598 struct smb_filename **pp_smb_fname_out)
2600 char *parent;
2601 const char *base;
2602 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2603 smb_fname_in);
2605 if (smb_fname == NULL) {
2606 return -1;
2609 /* We need streamname to be NULL */
2610 TALLOC_FREE(smb_fname->stream_name);
2612 /* And we're replacing base_name. */
2613 TALLOC_FREE(smb_fname->base_name);
2615 SET_STAT_INVALID(smb_fname->st);
2617 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2618 &parent, &base)) {
2619 TALLOC_FREE(smb_fname);
2620 return -1;
2623 smb_fname->base_name = talloc_asprintf(smb_fname,
2624 "%s/._%s", parent, base);
2625 if (smb_fname->base_name == NULL) {
2626 TALLOC_FREE(smb_fname);
2627 return -1;
2630 *pp_smb_fname_out = smb_fname;
2632 return 0;
2636 * Allocate and initialize an AfpInfo struct
2638 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2640 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2641 if (ai == NULL) {
2642 return NULL;
2644 ai->afpi_Signature = AFP_Signature;
2645 ai->afpi_Version = AFP_Version;
2646 ai->afpi_BackupTime = AD_DATE_START;
2647 return ai;
2651 * Pack an AfpInfo struct into a buffer
2653 * Buffer size must be at least AFP_INFO_SIZE
2654 * Returns size of packed buffer
2656 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2658 memset(buf, 0, AFP_INFO_SIZE);
2660 RSIVAL(buf, 0, ai->afpi_Signature);
2661 RSIVAL(buf, 4, ai->afpi_Version);
2662 RSIVAL(buf, 12, ai->afpi_BackupTime);
2663 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2665 return AFP_INFO_SIZE;
2669 * Unpack a buffer into a AfpInfo structure
2671 * Buffer size must be at least AFP_INFO_SIZE
2672 * Returns allocated AfpInfo struct
2674 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2676 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2677 if (ai == NULL) {
2678 return NULL;
2681 ai->afpi_Signature = RIVAL(data, 0);
2682 ai->afpi_Version = RIVAL(data, 4);
2683 ai->afpi_BackupTime = RIVAL(data, 12);
2684 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2685 sizeof(ai->afpi_FinderInfo));
2687 if (ai->afpi_Signature != AFP_Signature
2688 || ai->afpi_Version != AFP_Version) {
2689 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2690 TALLOC_FREE(ai);
2693 return ai;