script/autobuild.py: add support git worktree
[Samba.git] / source3 / lib / adouble.c
blob84198ab20006b0a9ad6c2cfc961474ee50ad2750
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->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;
1106 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
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;
1134 nwritten = SMB_VFS_PWRITE(fsp,
1135 ad->ad_data + e->adx_offset,
1136 e->adx_length,
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;
1147 status = close_file(NULL, fsp, NORMAL_CLOSE);
1148 if (!NT_STATUS_IS_OK(status)) {
1149 ok = false;
1150 goto fail;
1152 fsp = NULL;
1155 ad->adx_header.adx_num_attrs = 0;
1156 TALLOC_FREE(ad->adx_entries);
1158 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1160 rc = ad_fset(handle, ad, ad->ad_fsp);
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;
1168 ok = ad_convert_move_reso(handle, ad, smb_fname);
1169 if (!ok) {
1170 goto fail;
1173 *converted_xattr = true;
1174 ok = true;
1176 fail:
1177 return ok;
1180 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1181 struct adouble *ad,
1182 const struct smb_filename *smb_fname)
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;
1195 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1196 if (cmp != 0) {
1197 return true;
1200 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1201 if (p_ad == NULL) {
1202 return false;
1205 ai = afpinfo_new(talloc_tos());
1206 if (ai == NULL) {
1207 return false;
1210 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1212 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1213 if (aiblob.data == NULL) {
1214 TALLOC_FREE(ai);
1215 return false;
1218 size = afpinfo_pack(ai, (char *)aiblob.data);
1219 TALLOC_FREE(ai);
1220 if (size != AFP_INFO_SIZE) {
1221 return false;
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;
1235 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
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;
1262 nwritten = SMB_VFS_PWRITE(fsp,
1263 aiblob.data,
1264 aiblob.length,
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;
1274 status = close_file(NULL, fsp, NORMAL_CLOSE);
1275 if (!NT_STATUS_IS_OK(status)) {
1276 return false;
1278 fsp = NULL;
1280 return true;
1283 static bool ad_convert_truncate(vfs_handle_struct *handle,
1284 struct adouble *ad,
1285 const struct smb_filename *smb_fname)
1287 int rc;
1288 off_t newlen;
1290 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1292 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1293 if (rc != 0) {
1294 return false;
1297 return true;
1300 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1301 struct adouble *ad,
1302 uint32_t flags,
1303 bool *blank)
1305 size_t rforklen = sizeof(empty_resourcefork);
1306 char buf[rforklen];
1307 ssize_t nread;
1308 int cmp;
1309 int rc;
1311 *blank = false;
1313 if (!(flags & AD_CONV_WIPE_BLANK)) {
1314 return true;
1317 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1318 return true;
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;
1328 cmp = memcmp(buf, empty_resourcefork, rforklen);
1329 if (cmp != 0) {
1330 return true;
1333 ad_setentrylen(ad, ADEID_RFORK, 0);
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;
1341 *blank = true;
1342 return true;
1345 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1346 struct adouble *ad,
1347 struct files_struct *dirfsp,
1348 const struct smb_filename *smb_fname,
1349 uint32_t flags)
1351 struct smb_filename *ad_name = NULL;
1352 int rc;
1354 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1355 return true;
1358 if (!(flags & AD_CONV_DELETE)) {
1359 return true;
1362 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1363 if (rc != 0) {
1364 return false;
1367 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1368 dirfsp,
1369 ad_name,
1371 if (rc != 0) {
1372 DBG_ERR("Unlinking [%s] failed: %s\n",
1373 smb_fname_str_dbg(ad_name), strerror(errno));
1374 TALLOC_FREE(ad_name);
1375 return false;
1378 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1379 TALLOC_FREE(ad_name);
1381 return true;
1385 * Convert from Apple's ._ file to Netatalk
1387 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1388 * bytes containing packed xattrs.
1390 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1391 * otherwise
1393 int ad_convert(struct vfs_handle_struct *handle,
1394 struct files_struct *dirfsp,
1395 const struct smb_filename *smb_fname,
1396 const char *catia_mappings,
1397 uint32_t flags)
1399 struct adouble *ad = NULL;
1400 bool ok;
1401 bool converted_xattr = false;
1402 bool blank;
1403 int ret;
1405 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1406 if (ad == NULL) {
1407 return 0;
1410 ok = ad_convert_xattr(handle,
1412 smb_fname,
1413 catia_mappings,
1414 &converted_xattr);
1415 if (!ok) {
1416 ret = -1;
1417 goto done;
1420 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1421 if (!ok) {
1422 ret = -1;
1423 goto done;
1426 if (converted_xattr || blank) {
1427 ok = ad_convert_truncate(handle, ad, smb_fname);
1428 if (!ok) {
1429 ret = -1;
1430 goto done;
1434 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1435 if (!ok) {
1436 DBG_ERR("Failed to convert [%s]\n",
1437 smb_fname_str_dbg(smb_fname));
1438 ret = -1;
1439 goto done;
1442 ok = ad_convert_delete_adfile(handle,
1444 dirfsp,
1445 smb_fname,
1446 flags);
1447 if (!ok) {
1448 ret = -1;
1449 goto done;
1452 ret = 0;
1453 done:
1454 TALLOC_FREE(ad);
1455 return ret;
1458 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1459 struct vfs_handle_struct *handle,
1460 struct smb_filename *smb_fname,
1461 struct smb_filename *adpath,
1462 files_struct **_fsp)
1464 files_struct *fsp = NULL;
1465 NTSTATUS status;
1466 int ret;
1468 status = SMB_VFS_CREATE_FILE(
1469 handle->conn,
1470 NULL, /* req */
1471 0, /* root_dir_fid */
1472 adpath,
1473 FILE_READ_DATA|FILE_WRITE_DATA,
1474 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1475 FILE_OPEN_IF,
1476 0, /* create_options */
1477 0, /* file_attributes */
1478 INTERNAL_OPEN_ONLY,
1479 NULL, /* lease */
1480 0, /* allocation_size */
1481 0, /* private_flags */
1482 NULL, /* sd */
1483 NULL, /* ea_list */
1484 &fsp,
1485 NULL, /* info */
1486 NULL, NULL); /* create context */
1487 if (!NT_STATUS_IS_OK(status)) {
1488 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1489 smb_fname_str_dbg(adpath), nt_errstr(status));
1490 return false;
1493 if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1494 fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1496 ret = SMB_VFS_FCHOWN(fsp,
1497 smb_fname->st.st_ex_uid,
1498 smb_fname->st.st_ex_gid);
1499 if (ret != 0) {
1500 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1501 fsp_str_dbg(fsp), nt_errstr(status));
1502 close_file(NULL, fsp, NORMAL_CLOSE);
1503 return false;
1507 *_fsp = fsp;
1508 return true;
1511 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1512 struct smb_filename *smb_fname,
1513 TALLOC_CTX *mem_ctx,
1514 unsigned int *num_streams,
1515 struct stream_struct **streams)
1517 files_struct *fsp = NULL;
1518 NTSTATUS status;
1520 status = SMB_VFS_CREATE_FILE(
1521 handle->conn, /* conn */
1522 NULL, /* req */
1523 0, /* root_dir_fid */
1524 smb_fname, /* fname */
1525 FILE_READ_ATTRIBUTES, /* access_mask */
1526 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1527 FILE_SHARE_DELETE),
1528 FILE_OPEN, /* create_disposition*/
1529 0, /* create_options */
1530 0, /* file_attributes */
1531 INTERNAL_OPEN_ONLY, /* oplock_request */
1532 NULL, /* lease */
1533 0, /* allocation_size */
1534 0, /* private_flags */
1535 NULL, /* sd */
1536 NULL, /* ea_list */
1537 &fsp, /* result */
1538 NULL, /* pinfo */
1539 NULL, NULL); /* create context */
1540 if (!NT_STATUS_IS_OK(status)) {
1541 DBG_ERR("Opening [%s] failed: %s\n",
1542 smb_fname_str_dbg(smb_fname),
1543 nt_errstr(status));
1544 return false;
1547 status = vfs_streaminfo(handle->conn,
1548 fsp,
1549 fsp->fsp_name,
1550 mem_ctx,
1551 num_streams,
1552 streams);
1553 if (!NT_STATUS_IS_OK(status)) {
1554 close_file(NULL, fsp, NORMAL_CLOSE);
1555 DBG_ERR("streaminfo on [%s] failed: %s\n",
1556 smb_fname_str_dbg(smb_fname),
1557 nt_errstr(status));
1558 return false;
1561 status = close_file(NULL, fsp, NORMAL_CLOSE);
1562 if (!NT_STATUS_IS_OK(status)) {
1563 DBG_ERR("close_file [%s] failed: %s\n",
1564 smb_fname_str_dbg(smb_fname),
1565 nt_errstr(status));
1566 return false;
1569 return true;
1572 struct ad_collect_state {
1573 bool have_adfile;
1574 size_t adx_data_off;
1575 char *rsrc_data_buf;
1578 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1579 struct char_mappings **cmaps,
1580 struct smb_filename *smb_fname,
1581 const struct stream_struct *stream,
1582 struct adouble *ad,
1583 struct ad_collect_state *state)
1585 struct smb_filename *sname = NULL;
1586 files_struct *fsp = NULL;
1587 struct ad_xattr_entry *e = NULL;
1588 char *mapped_name = NULL;
1589 char *p = NULL;
1590 size_t needed_size;
1591 ssize_t nread;
1592 NTSTATUS status;
1593 int ret;
1594 bool ok;
1596 sname = synthetic_smb_fname(ad,
1597 smb_fname->base_name,
1598 stream->name,
1599 NULL,
1601 if (sname == NULL) {
1602 return false;
1605 if (is_ntfs_default_stream_smb_fname(sname)) {
1606 TALLOC_FREE(sname);
1607 return true;
1610 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1612 ret = SMB_VFS_STAT(handle->conn, sname);
1613 if (ret != 0) {
1614 DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
1615 ok = false;
1616 goto out;
1619 status = SMB_VFS_CREATE_FILE(
1620 handle->conn,
1621 NULL, /* req */
1622 0, /* root_dir_fid */
1623 sname,
1624 FILE_READ_DATA|DELETE_ACCESS,
1625 FILE_SHARE_READ,
1626 FILE_OPEN,
1627 0, /* create_options */
1628 0, /* file_attributes */
1629 INTERNAL_OPEN_ONLY, /* oplock_request */
1630 NULL, /* lease */
1631 0, /* allocation_size */
1632 0, /* private_flags */
1633 NULL, /* sd */
1634 NULL, /* ea_list */
1635 &fsp,
1636 NULL, /* info */
1637 NULL, NULL); /* create context */
1638 if (!NT_STATUS_IS_OK(status)) {
1639 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1640 smb_fname_str_dbg(sname));
1641 ok = false;
1642 goto out;
1645 if (is_afpinfo_stream(stream->name)) {
1646 char buf[AFP_INFO_SIZE];
1648 if (stream->size != AFP_INFO_SIZE) {
1649 DBG_ERR("Bad size [%zd] on [%s]\n",
1650 (ssize_t)stream->size,
1651 smb_fname_str_dbg(sname));
1652 ok = false;
1653 goto out;
1656 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1657 if (nread != AFP_INFO_SIZE) {
1658 DBG_ERR("Bad size [%zd] on [%s]\n",
1659 (ssize_t)stream->size,
1660 smb_fname_str_dbg(sname));
1661 ok = false;
1662 goto out;
1665 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1666 buf + AFP_OFF_FinderInfo,
1667 AFP_FinderSize);
1669 ok = set_delete_on_close(fsp,
1670 true,
1671 fsp->conn->session_info->security_token,
1672 fsp->conn->session_info->unix_token);
1673 if (!ok) {
1674 DBG_ERR("Deleting [%s] failed\n",
1675 smb_fname_str_dbg(sname));
1676 ok = false;
1677 goto out;
1679 ok = true;
1680 goto out;
1683 if (is_afpresource_stream(stream->name)) {
1684 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1685 if (ad->ad_rsrc_data == NULL) {
1686 ok = false;
1687 goto out;
1690 nread = SMB_VFS_PREAD(fsp,
1691 ad->ad_rsrc_data,
1692 stream->size,
1694 if (nread != stream->size) {
1695 DBG_ERR("Bad size [%zd] on [%s]\n",
1696 (ssize_t)stream->size,
1697 smb_fname_str_dbg(sname));
1698 ok = false;
1699 goto out;
1702 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1704 if (!state->have_adfile) {
1706 * We have a resource *stream* but no AppleDouble
1707 * sidecar file, this means the share is configured with
1708 * fruit:resource=stream. So we should delete the
1709 * resource stream.
1711 ok = set_delete_on_close(
1712 fsp,
1713 true,
1714 fsp->conn->session_info->security_token,
1715 fsp->conn->session_info->unix_token);
1716 if (!ok) {
1717 DBG_ERR("Deleting [%s] failed\n",
1718 smb_fname_str_dbg(sname));
1719 ok = false;
1720 goto out;
1723 ok = true;
1724 goto out;
1727 ad->adx_entries = talloc_realloc(ad,
1728 ad->adx_entries,
1729 struct ad_xattr_entry,
1730 ad->adx_header.adx_num_attrs + 1);
1731 if (ad->adx_entries == NULL) {
1732 ok = false;
1733 goto out;
1736 e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1737 *e = (struct ad_xattr_entry) {
1738 .adx_length = stream->size,
1740 e->adx_name = talloc_strdup(ad, stream->name + 1);
1741 if (e->adx_name == NULL) {
1742 ok = false;
1743 goto out;
1745 p = strchr(e->adx_name, ':');
1746 if (p != NULL) {
1747 *p = '\0';
1750 status = string_replace_allocate(handle->conn,
1751 e->adx_name,
1752 cmaps,
1754 &mapped_name,
1755 vfs_translate_to_unix);
1756 if (!NT_STATUS_IS_OK(status) &&
1757 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1759 DBG_ERR("string_replace_allocate failed\n");
1760 ok = false;
1761 goto out;
1764 e->adx_name = mapped_name;
1765 e->adx_namelen = strlen(e->adx_name) + 1,
1767 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1768 ad->adx_header.adx_num_attrs,
1769 e->adx_name,
1770 (size_t)e->adx_length);
1772 ad->adx_header.adx_num_attrs++;
1774 needed_size = state->adx_data_off + stream->size;
1775 if (needed_size > talloc_get_size(ad->adx_data)) {
1776 ad->adx_data = talloc_realloc(ad,
1777 ad->adx_data,
1778 char,
1779 needed_size);
1780 if (ad->adx_data == NULL) {
1781 ok = false;
1782 goto out;
1786 nread = SMB_VFS_PREAD(fsp,
1787 ad->adx_data + state->adx_data_off,
1788 stream->size,
1790 if (nread != stream->size) {
1791 DBG_ERR("Bad size [%zd] on [%s]\n",
1792 (ssize_t)stream->size,
1793 smb_fname_str_dbg(sname));
1794 ok = false;
1795 goto out;
1797 state->adx_data_off += nread;
1799 ok = set_delete_on_close(fsp,
1800 true,
1801 fsp->conn->session_info->security_token,
1802 fsp->conn->session_info->unix_token);
1803 if (!ok) {
1804 DBG_ERR("Deleting [%s] failed\n",
1805 smb_fname_str_dbg(sname));
1806 ok = false;
1807 goto out;
1810 out:
1811 TALLOC_FREE(sname);
1812 if (fsp != NULL) {
1813 status = close_file(NULL, fsp, NORMAL_CLOSE);
1814 if (!NT_STATUS_IS_OK(status)) {
1815 DBG_ERR("close_file [%s] failed: %s\n",
1816 smb_fname_str_dbg(smb_fname),
1817 nt_errstr(status));
1818 ok = false;
1822 return ok;
1826 * Convert filesystem metadata to AppleDouble file
1828 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1829 struct vfs_handle_struct *handle,
1830 const char *catia_mappings,
1831 struct smb_filename *smb_fname,
1832 bool *converted)
1834 static struct char_mappings **cmaps = NULL;
1835 TALLOC_CTX *frame = talloc_stackframe();
1836 struct ad_collect_state state;
1837 struct stream_struct *streams = NULL;
1838 struct smb_filename *adpath = NULL;
1839 struct adouble *ad = NULL;
1840 unsigned int num_streams = 0;
1841 size_t to_convert = 0;
1842 bool have_rsrc;
1843 files_struct *fsp = NULL;
1844 size_t i;
1845 NTSTATUS status;
1846 int ret;
1847 bool ok;
1849 *converted = false;
1851 if (cmaps == NULL) {
1852 const char **mappings = NULL;
1854 mappings = str_list_make_v3_const(
1855 frame, catia_mappings, NULL);
1856 if (mappings == NULL) {
1857 ok = false;
1858 goto out;
1860 cmaps = string_replace_init_map(mem_ctx, mappings);
1861 TALLOC_FREE(mappings);
1864 ok = ad_unconvert_get_streams(handle,
1865 smb_fname,
1866 frame,
1867 &num_streams,
1868 &streams);
1869 if (!ok) {
1870 goto out;
1873 for (i = 0; i < num_streams; i++) {
1874 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
1875 continue;
1877 to_convert++;
1878 if (is_afpresource_stream(streams[i].name)) {
1879 have_rsrc = true;
1883 if (to_convert == 0) {
1884 ok = true;
1885 goto out;
1888 state = (struct ad_collect_state) {
1889 .adx_data_off = 0,
1892 ret = adouble_path(frame, smb_fname, &adpath);
1893 if (ret != 0) {
1894 ok = false;
1895 goto out;
1898 ret = SMB_VFS_STAT(handle->conn, adpath);
1899 if (ret == 0) {
1900 state.have_adfile = true;
1901 } else {
1902 if (errno != ENOENT) {
1903 ok = false;
1904 goto out;
1906 state.have_adfile = false;
1909 if (to_convert == 1 && have_rsrc && state.have_adfile) {
1911 * So we have just a single stream, the resource fork stream
1912 * from an AppleDouble file. Fine, that means there's nothing to
1913 * convert.
1915 ok = true;
1916 goto out;
1919 ad = ad_init(frame, ADOUBLE_RSRC);
1920 if (ad == NULL) {
1921 ok = false;
1922 goto out;
1925 for (i = 0; i < num_streams; i++) {
1926 ok = ad_collect_one_stream(handle,
1927 cmaps,
1928 smb_fname,
1929 &streams[i],
1931 &state);
1932 if (!ok) {
1933 goto out;
1937 ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
1938 if (!ok) {
1939 DBG_ERR("Failed to open adfile [%s]\n",
1940 smb_fname_str_dbg(smb_fname));
1941 goto out;
1944 ret = ad_fset(handle, ad, fsp);
1945 if (ret != 0) {
1946 ok = false;
1947 goto out;
1950 *converted = true;
1951 ok = true;
1953 out:
1954 if (fsp != NULL) {
1955 status = close_file(NULL, fsp, NORMAL_CLOSE);
1956 if (!NT_STATUS_IS_OK(status)) {
1957 DBG_ERR("close_file [%s] failed: %s\n",
1958 smb_fname_str_dbg(smb_fname),
1959 nt_errstr(status));
1960 ok = false;
1963 TALLOC_FREE(frame);
1964 return ok;
1968 * Read and parse Netatalk AppleDouble metadata xattr
1970 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1971 struct adouble *ad,
1972 const struct smb_filename *smb_fname)
1974 int rc = 0;
1975 ssize_t ealen;
1976 bool ok;
1978 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1980 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1981 AFPINFO_EA_NETATALK, ad->ad_data,
1982 AD_DATASZ_XATTR);
1983 if (ealen == -1) {
1984 switch (errno) {
1985 case ENOATTR:
1986 case ENOENT:
1987 if (errno == ENOATTR) {
1988 errno = ENOENT;
1990 rc = -1;
1991 goto exit;
1992 default:
1993 DEBUG(2, ("error reading meta xattr: %s\n",
1994 strerror(errno)));
1995 rc = -1;
1996 goto exit;
1999 if (ealen != AD_DATASZ_XATTR) {
2000 DEBUG(2, ("bad size %zd\n", ealen));
2001 errno = EINVAL;
2002 rc = -1;
2003 goto exit;
2006 /* Now parse entries */
2007 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2008 if (!ok) {
2009 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2010 errno = EINVAL;
2011 rc = -1;
2012 goto exit;
2015 if (!ad_getentryoff(ad, ADEID_FINDERI)
2016 || !ad_getentryoff(ad, ADEID_COMMENT)
2017 || !ad_getentryoff(ad, ADEID_FILEDATESI)
2018 || !ad_getentryoff(ad, ADEID_AFPFILEI)
2019 || !ad_getentryoff(ad, ADEID_PRIVDEV)
2020 || !ad_getentryoff(ad, ADEID_PRIVINO)
2021 || !ad_getentryoff(ad, ADEID_PRIVSYN)
2022 || !ad_getentryoff(ad, ADEID_PRIVID)) {
2023 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2024 errno = EINVAL;
2025 rc = -1;
2026 goto exit;
2029 exit:
2030 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2031 smb_fname->base_name, rc));
2033 if (rc != 0) {
2034 ealen = -1;
2035 if (errno == EINVAL) {
2036 become_root();
2037 (void)SMB_VFS_REMOVEXATTR(handle->conn,
2038 smb_fname,
2039 AFPINFO_EA_NETATALK);
2040 unbecome_root();
2041 errno = ENOENT;
2044 return ealen;
2047 static int ad_open_rsrc(vfs_handle_struct *handle,
2048 const struct smb_filename *smb_fname,
2049 int flags,
2050 mode_t mode,
2051 files_struct **_fsp)
2053 int ret;
2054 struct smb_filename *adp_smb_fname = NULL;
2055 files_struct *fsp = NULL;
2056 uint32_t access_mask;
2057 uint32_t share_access;
2058 uint32_t create_disposition;
2059 NTSTATUS status;
2061 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2062 if (ret != 0) {
2063 return -1;
2066 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
2067 if (ret != 0) {
2068 TALLOC_FREE(adp_smb_fname);
2069 return -1;
2072 access_mask = FILE_GENERIC_READ;
2073 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
2074 create_disposition = FILE_OPEN;
2076 if (flags & O_RDWR) {
2077 access_mask |= FILE_GENERIC_WRITE;
2078 share_access &= ~FILE_SHARE_WRITE;
2081 status = SMB_VFS_CREATE_FILE(
2082 handle->conn, /* conn */
2083 NULL, /* req */
2084 0, /* root_dir_fid */
2085 adp_smb_fname,
2086 access_mask,
2087 share_access,
2088 create_disposition,
2089 0, /* create_options */
2090 0, /* file_attributes */
2091 INTERNAL_OPEN_ONLY, /* oplock_request */
2092 NULL, /* lease */
2093 0, /* allocation_size */
2094 0, /* private_flags */
2095 NULL, /* sd */
2096 NULL, /* ea_list */
2097 &fsp,
2098 NULL, /* psbuf */
2099 NULL, NULL); /* create context */
2100 TALLOC_FREE(adp_smb_fname);
2101 if (!NT_STATUS_IS_OK(status)) {
2102 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
2103 return -1;
2106 *_fsp = fsp;
2107 return 0;
2111 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2112 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2113 * for file IO on the ._ file.
2115 static int ad_open(vfs_handle_struct *handle,
2116 struct adouble *ad,
2117 files_struct *fsp,
2118 const struct smb_filename *smb_fname,
2119 int flags,
2120 mode_t mode)
2122 int ret;
2124 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2125 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2127 if (ad->ad_type == ADOUBLE_META) {
2128 return 0;
2131 if (fsp != NULL) {
2132 ad->ad_fsp = fsp;
2133 ad->ad_opened = false;
2134 return 0;
2137 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
2138 if (ret != 0) {
2139 return -1;
2141 ad->ad_opened = true;
2143 DBG_DEBUG("Path [%s] type [%s]\n",
2144 smb_fname->base_name,
2145 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2147 return 0;
2150 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2151 struct adouble *ad,
2152 const struct smb_filename *smb_fname)
2154 size_t to_read;
2155 ssize_t len;
2156 int ret;
2157 bool ok;
2159 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2160 if (ret != 0) {
2161 DBG_ERR("fstat [%s] failed: %s\n",
2162 fsp_str_dbg(ad->ad_fsp), strerror(errno));
2163 return -1;
2166 to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2167 if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2168 to_read = AD_XATTR_MAX_HDR_SIZE;
2171 len = SMB_VFS_NEXT_PREAD(handle,
2172 ad->ad_fsp,
2173 ad->ad_data,
2174 to_read,
2176 if (len != to_read) {
2177 DBG_NOTICE("%s %s: bad size: %zd\n",
2178 smb_fname->base_name, strerror(errno), len);
2179 return -1;
2182 /* Now parse entries */
2183 ok = ad_unpack(ad,
2184 ADEID_NUM_DOT_UND,
2185 ad->ad_fsp->fsp_name->st.st_ex_size);
2186 if (!ok) {
2187 DBG_ERR("invalid AppleDouble resource %s\n",
2188 smb_fname->base_name);
2189 errno = EINVAL;
2190 return -1;
2193 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2194 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2195 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2197 DBG_ERR("invalid AppleDouble resource %s\n",
2198 smb_fname->base_name);
2199 errno = EINVAL;
2200 return -1;
2203 return len;
2207 * Read and parse resource fork, either ._ AppleDouble file or xattr
2209 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2210 struct adouble *ad,
2211 const struct smb_filename *smb_fname)
2213 return ad_read_rsrc_adouble(handle, ad, smb_fname);
2217 * Read and unpack an AppleDouble metadata xattr or resource
2219 static ssize_t ad_read(vfs_handle_struct *handle,
2220 struct adouble *ad,
2221 const struct smb_filename *smb_fname)
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;
2233 static int adouble_destructor(struct adouble *ad)
2235 NTSTATUS status;
2237 if (!ad->ad_opened) {
2238 return 0;
2241 SMB_ASSERT(ad->ad_fsp != NULL);
2243 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
2244 if (!NT_STATUS_IS_OK(status)) {
2245 DBG_ERR("Closing [%s] failed: %s\n",
2246 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2249 return 0;
2253 * Allocate a struct adouble without initialiing it
2255 * The struct is either hang of the fsp extension context or if fsp is
2256 * NULL from ctx.
2258 * @param[in] ctx talloc context
2259 * @param[in] handle vfs handle
2260 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2262 * @return adouble handle
2264 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2265 adouble_type_t type)
2267 int rc = 0;
2268 size_t adsize = 0;
2269 struct adouble *ad;
2271 switch (type) {
2272 case ADOUBLE_META:
2273 adsize = AD_DATASZ_XATTR;
2274 break;
2275 case ADOUBLE_RSRC:
2277 * AppleDouble ._ file case, optimize for fewer (but larger)
2278 * IOs. Two cases:
2280 * - without xattrs size of the header is exactly
2281 * AD_DATASZ_DOT_UND (82) bytes
2283 * - with embedded xattrs it can be larger, up to
2284 * AD_XATTR_MAX_HDR_SIZE
2286 * Larger headers are not supported, but this is a reasonable
2287 * limit that is also employed by the macOS client.
2289 * We used the largest possible size to be able to read the full
2290 * header with one IO.
2292 adsize = AD_XATTR_MAX_HDR_SIZE;
2293 break;
2294 default:
2295 return NULL;
2298 ad = talloc_zero(ctx, struct adouble);
2299 if (ad == NULL) {
2300 rc = -1;
2301 goto exit;
2304 if (adsize) {
2305 ad->ad_data = talloc_zero_array(ad, char, adsize);
2306 if (ad->ad_data == NULL) {
2307 rc = -1;
2308 goto exit;
2312 ad->ad_type = type;
2313 ad->ad_magic = AD_MAGIC;
2314 ad->ad_version = AD_VERSION;
2316 talloc_set_destructor(ad, adouble_destructor);
2318 exit:
2319 if (rc != 0) {
2320 TALLOC_FREE(ad);
2322 return ad;
2326 * Allocate and initialize a new struct adouble
2328 * @param[in] ctx talloc context
2329 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2331 * @return adouble handle, initialized
2333 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2335 int rc = 0;
2336 const struct ad_entry_order *eid;
2337 struct adouble *ad = NULL;
2338 time_t t = time(NULL);
2340 switch (type) {
2341 case ADOUBLE_META:
2342 eid = entry_order_meta_xattr;
2343 break;
2344 case ADOUBLE_RSRC:
2345 eid = entry_order_dot_und;
2346 break;
2347 default:
2348 return NULL;
2351 ad = ad_alloc(ctx, type);
2352 if (ad == NULL) {
2353 return NULL;
2356 while (eid->id) {
2357 ad->ad_eid[eid->id].ade_off = eid->offset;
2358 ad->ad_eid[eid->id].ade_len = eid->len;
2359 eid++;
2362 /* put something sane in the date fields */
2363 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2364 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2365 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2366 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2368 if (rc != 0) {
2369 TALLOC_FREE(ad);
2371 return ad;
2374 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2375 vfs_handle_struct *handle,
2376 files_struct *fsp,
2377 const struct smb_filename *smb_fname,
2378 adouble_type_t type)
2380 int rc = 0;
2381 ssize_t len;
2382 struct adouble *ad = NULL;
2383 int mode;
2385 if (fsp != NULL) {
2386 smb_fname = fsp->base_fsp->fsp_name;
2389 DEBUG(10, ("ad_get(%s) called for %s\n",
2390 type == ADOUBLE_META ? "meta" : "rsrc",
2391 smb_fname->base_name));
2393 ad = ad_alloc(ctx, type);
2394 if (ad == NULL) {
2395 rc = -1;
2396 goto exit;
2399 /* Try rw first so we can use the fd in ad_convert() */
2400 mode = O_RDWR;
2402 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2403 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2404 mode = O_RDONLY;
2405 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2407 if (rc == -1) {
2408 DBG_DEBUG("ad_open [%s] error [%s]\n",
2409 smb_fname->base_name, strerror(errno));
2410 goto exit;
2414 len = ad_read(handle, ad, smb_fname);
2415 if (len == -1) {
2416 DEBUG(10, ("error reading AppleDouble for %s\n",
2417 smb_fname->base_name));
2418 rc = -1;
2419 goto exit;
2422 exit:
2423 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2424 type == ADOUBLE_META ? "meta" : "rsrc",
2425 smb_fname->base_name, rc));
2427 if (rc != 0) {
2428 TALLOC_FREE(ad);
2430 return ad;
2434 * Return AppleDouble data for a file
2436 * @param[in] ctx talloc context
2437 * @param[in] handle vfs handle
2438 * @param[in] smb_fname pathname to file or directory
2439 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2441 * @return talloced struct adouble or NULL on error
2443 struct adouble *ad_get(TALLOC_CTX *ctx,
2444 vfs_handle_struct *handle,
2445 const struct smb_filename *smb_fname,
2446 adouble_type_t type)
2448 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2452 * Return AppleDouble data for a file
2454 * @param[in] ctx talloc context
2455 * @param[in] handle vfs handle
2456 * @param[in] fsp fsp to use for IO
2457 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2459 * @return talloced struct adouble or NULL on error
2461 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2462 files_struct *fsp, adouble_type_t type)
2464 return ad_get_internal(ctx, handle, fsp, NULL, type);
2468 * Set AppleDouble metadata on a file or directory
2470 * @param[in] ad adouble handle
2472 * @param[in] smb_fname pathname to file or directory
2474 * @return status code, 0 means success
2476 int ad_set(vfs_handle_struct *handle,
2477 struct adouble *ad,
2478 const struct smb_filename *smb_fname)
2480 bool ok;
2481 int ret;
2483 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2485 if (ad->ad_type != ADOUBLE_META) {
2486 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2487 smb_fname->base_name);
2488 return -1;
2491 ok = ad_pack(handle, ad, NULL);
2492 if (!ok) {
2493 return -1;
2496 ret = SMB_VFS_SETXATTR(handle->conn,
2497 smb_fname,
2498 AFPINFO_EA_NETATALK,
2499 ad->ad_data,
2500 AD_DATASZ_XATTR, 0);
2502 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2504 return ret;
2508 * Set AppleDouble metadata on a file or directory
2510 * @param[in] ad adouble handle
2511 * @param[in] fsp file handle
2513 * @return status code, 0 means success
2515 int ad_fset(struct vfs_handle_struct *handle,
2516 struct adouble *ad,
2517 files_struct *fsp)
2519 int rc = -1;
2520 ssize_t len;
2521 bool ok;
2523 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2525 if ((fsp == NULL)
2526 || (fsp->fh == NULL)
2527 || (fsp->fh->fd == -1))
2529 smb_panic("bad fsp");
2532 ok = ad_pack(handle, ad, fsp);
2533 if (!ok) {
2534 return -1;
2537 switch (ad->ad_type) {
2538 case ADOUBLE_META:
2539 rc = SMB_VFS_NEXT_SETXATTR(handle,
2540 fsp->fsp_name,
2541 AFPINFO_EA_NETATALK,
2542 ad->ad_data,
2543 AD_DATASZ_XATTR, 0);
2544 break;
2546 case ADOUBLE_RSRC:
2547 len = SMB_VFS_NEXT_PWRITE(handle,
2548 fsp,
2549 ad->ad_data,
2550 ad_getentryoff(ad, ADEID_RFORK),
2552 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2553 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2554 return -1;
2556 rc = 0;
2557 break;
2559 default:
2560 return -1;
2563 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2565 return rc;
2568 bool is_adouble_file(const char *path)
2570 const char *p = NULL;
2571 int match;
2573 p = strrchr(path, '/');
2574 if (p == NULL) {
2575 p = path;
2576 } else {
2577 p++;
2580 match = strncmp(p,
2581 ADOUBLE_NAME_PREFIX,
2582 strlen(ADOUBLE_NAME_PREFIX));
2583 if (match != 0) {
2584 return false;
2586 return true;
2590 * Prepend "._" to a basename
2591 * Return a new struct smb_filename with stream_name == NULL.
2593 int adouble_path(TALLOC_CTX *ctx,
2594 const struct smb_filename *smb_fname_in,
2595 struct smb_filename **pp_smb_fname_out)
2597 char *parent;
2598 const char *base;
2599 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2600 smb_fname_in);
2602 if (smb_fname == NULL) {
2603 return -1;
2606 /* We need streamname to be NULL */
2607 TALLOC_FREE(smb_fname->stream_name);
2609 /* And we're replacing base_name. */
2610 TALLOC_FREE(smb_fname->base_name);
2612 SET_STAT_INVALID(smb_fname->st);
2614 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2615 &parent, &base)) {
2616 TALLOC_FREE(smb_fname);
2617 return -1;
2620 smb_fname->base_name = talloc_asprintf(smb_fname,
2621 "%s/._%s", parent, base);
2622 if (smb_fname->base_name == NULL) {
2623 TALLOC_FREE(smb_fname);
2624 return -1;
2627 *pp_smb_fname_out = smb_fname;
2629 return 0;
2633 * Allocate and initialize an AfpInfo struct
2635 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2637 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2638 if (ai == NULL) {
2639 return NULL;
2641 ai->afpi_Signature = AFP_Signature;
2642 ai->afpi_Version = AFP_Version;
2643 ai->afpi_BackupTime = AD_DATE_START;
2644 return ai;
2648 * Pack an AfpInfo struct into a buffer
2650 * Buffer size must be at least AFP_INFO_SIZE
2651 * Returns size of packed buffer
2653 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2655 memset(buf, 0, AFP_INFO_SIZE);
2657 RSIVAL(buf, 0, ai->afpi_Signature);
2658 RSIVAL(buf, 4, ai->afpi_Version);
2659 RSIVAL(buf, 12, ai->afpi_BackupTime);
2660 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2662 return AFP_INFO_SIZE;
2666 * Unpack a buffer into a AfpInfo structure
2668 * Buffer size must be at least AFP_INFO_SIZE
2669 * Returns allocated AfpInfo struct
2671 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2673 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2674 if (ai == NULL) {
2675 return NULL;
2678 ai->afpi_Signature = RIVAL(data, 0);
2679 ai->afpi_Version = RIVAL(data, 4);
2680 ai->afpi_BackupTime = RIVAL(data, 12);
2681 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2682 sizeof(ai->afpi_FinderInfo));
2684 if (ai->afpi_Signature != AFP_Signature
2685 || ai->afpi_Version != AFP_Version) {
2686 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2687 TALLOC_FREE(ai);
2690 return ai;