rpc_server3: Use fdopen_keepfd()
[Samba.git] / source3 / lib / adouble.c
blob1e8dbc4aaf3cef2fd29e59c014396b3f90e01dc2
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 * preceded 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 * All entries besides FinderInfo and resource fork must fit into the
274 * buffer. FinderInfo is special as it may be larger then the default 32 bytes
275 * if it contains marshalled xattrs, which we will fixup that in
276 * ad_convert(). The first 32 bytes however must also be part of the buffer.
278 * The resource fork is never accessed directly by the ad_data buf.
280 static bool ad_entry_check_size(uint32_t eid,
281 size_t bufsize,
282 uint32_t off,
283 uint32_t got_len)
285 struct {
286 off_t expected_len;
287 bool fixed_size;
288 bool minimum_size;
289 } ad_checks[] = {
290 [ADEID_DFORK] = {-1, false, false}, /* not applicable */
291 [ADEID_RFORK] = {-1, false, false}, /* no limit */
292 [ADEID_NAME] = {ADEDLEN_NAME, false, false},
293 [ADEID_COMMENT] = {ADEDLEN_COMMENT, false, false},
294 [ADEID_ICONBW] = {ADEDLEN_ICONBW, true, false},
295 [ADEID_ICONCOL] = {ADEDLEN_ICONCOL, false, false},
296 [ADEID_FILEI] = {ADEDLEN_FILEI, true, false},
297 [ADEID_FILEDATESI] = {ADEDLEN_FILEDATESI, true, false},
298 [ADEID_FINDERI] = {ADEDLEN_FINDERI, false, true},
299 [ADEID_MACFILEI] = {ADEDLEN_MACFILEI, true, false},
300 [ADEID_PRODOSFILEI] = {ADEDLEN_PRODOSFILEI, true, false},
301 [ADEID_MSDOSFILEI] = {ADEDLEN_MSDOSFILEI, true, false},
302 [ADEID_SHORTNAME] = {ADEDLEN_SHORTNAME, false, false},
303 [ADEID_AFPFILEI] = {ADEDLEN_AFPFILEI, true, false},
304 [ADEID_DID] = {ADEDLEN_DID, true, false},
305 [ADEID_PRIVDEV] = {ADEDLEN_PRIVDEV, true, false},
306 [ADEID_PRIVINO] = {ADEDLEN_PRIVINO, true, false},
307 [ADEID_PRIVSYN] = {ADEDLEN_PRIVSYN, true, false},
308 [ADEID_PRIVID] = {ADEDLEN_PRIVID, true, false},
311 if (eid >= ADEID_MAX) {
312 return false;
314 if (got_len == 0) {
315 /* Entry present, but empty, allow */
316 return true;
318 if (ad_checks[eid].expected_len == 0) {
320 * Shouldn't happen: implicitly initialized to zero because
321 * explicit initializer missing.
323 return false;
325 if (ad_checks[eid].expected_len == -1) {
326 /* Unused or no limit */
327 return true;
329 if (ad_checks[eid].fixed_size) {
330 if (ad_checks[eid].expected_len != got_len) {
331 /* Wrong size for fixed size entry. */
332 return false;
334 } else {
335 if (ad_checks[eid].minimum_size) {
336 if (got_len < ad_checks[eid].expected_len) {
338 * Too small for variable sized entry with
339 * minimum size.
341 return false;
343 } else {
344 if (got_len > ad_checks[eid].expected_len) {
345 /* Too big for variable sized entry. */
346 return false;
350 if (off + got_len < off) {
351 /* wrap around */
352 return false;
354 if (off + got_len > bufsize) {
355 /* overflow */
356 return false;
358 return true;
362 * Return a pointer to an AppleDouble entry
364 * Returns NULL if the entry is not present
366 char *ad_get_entry(const struct adouble *ad, int eid)
368 size_t bufsize = talloc_get_size(ad->ad_data);
369 off_t off = ad_getentryoff(ad, eid);
370 size_t len = ad_getentrylen(ad, eid);
371 bool valid;
373 valid = ad_entry_check_size(eid, bufsize, off, len);
374 if (!valid) {
375 return NULL;
378 if (off == 0 || len == 0) {
379 return NULL;
382 return ad->ad_data + off;
386 * Get a date
388 int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
390 bool xlate = (dateoff & AD_DATE_UNIX);
391 char *p = NULL;
393 dateoff &= AD_DATE_MASK;
394 p = ad_get_entry(ad, ADEID_FILEDATESI);
395 if (p == NULL) {
396 return -1;
399 if (dateoff > AD_DATE_ACCESS) {
400 return -1;
403 memcpy(date, p + dateoff, sizeof(uint32_t));
405 if (xlate) {
406 *date = AD_DATE_TO_UNIX(*date);
408 return 0;
412 * Set a date
414 int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
416 bool xlate = (dateoff & AD_DATE_UNIX);
417 char *p = NULL;
419 p = ad_get_entry(ad, ADEID_FILEDATESI);
420 if (p == NULL) {
421 return -1;
424 dateoff &= AD_DATE_MASK;
425 if (xlate) {
426 date = AD_DATE_FROM_UNIX(date);
429 if (dateoff > AD_DATE_ACCESS) {
430 return -1;
433 memcpy(p + dateoff, &date, sizeof(date));
435 return 0;
440 * Map on-disk AppleDouble id to enumerated id
442 static uint32_t get_eid(uint32_t eid)
444 if (eid <= 15) {
445 return eid;
448 switch (eid) {
449 case AD_DEV:
450 return ADEID_PRIVDEV;
451 case AD_INO:
452 return ADEID_PRIVINO;
453 case AD_SYN:
454 return ADEID_PRIVSYN;
455 case AD_ID:
456 return ADEID_PRIVID;
457 default:
458 break;
461 return 0;
465 * Move resourcefork data in an AppleDouble file
467 * This is supposed to make room in an AppleDouble file by moving the
468 * resourcefork data behind the space required for packing additional xattr data
469 * in the extended FinderInfo entry.
471 * When we're called we're expecting an AppleDouble file with just two entries
472 * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed
473 * offset of ADEDOFF_RFORK_DOT_UND.
475 static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
476 struct adouble *ad,
477 files_struct *fsp)
479 size_t reso_len;
480 size_t reso_off;
481 size_t n;
482 bool ok;
484 reso_len = ad_getentrylen(ad, ADEID_RFORK);
485 reso_off = ad_getentryoff(ad, ADEID_RFORK);
487 if (reso_len == 0) {
488 return true;
491 if (ad->ad_rsrc_data == NULL) {
493 * This buffer is already set when converting a resourcefork
494 * stream from vfs_streams_depot backend via ad_unconvert(). It
495 * is NULL with vfs_streams_xattr where the resourcefork stream
496 * is stored in an AppleDouble sidecar file vy vfs_fruit.
498 ad->ad_rsrc_data = talloc_size(ad, reso_len);
499 if (ad->ad_rsrc_data == NULL) {
500 return false;
503 n = SMB_VFS_NEXT_PREAD(handle,
504 fsp,
505 ad->ad_rsrc_data,
506 reso_len,
507 ADEDOFF_RFORK_DOT_UND);
508 if (n != reso_len) {
509 DBG_ERR("Read on [%s] failed\n",
510 fsp_str_dbg(fsp));
511 ok = false;
512 goto out;
516 n = SMB_VFS_NEXT_PWRITE(handle,
517 fsp,
518 ad->ad_rsrc_data,
519 reso_len,
520 reso_off);
521 if (n != reso_len) {
522 DBG_ERR("Write on [%s] failed\n",
523 fsp_str_dbg(fsp));
524 ok = false;
525 goto out;
528 ok = true;
529 out:
530 return ok;
533 static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
534 struct adouble *ad,
535 files_struct *fsp)
537 struct ad_xattr_header *h = &ad->adx_header;
538 size_t oldsize;
539 uint32_t off;
540 uint32_t data_off;
541 uint16_t i;
542 bool ok;
544 if (ad->adx_entries == NULL) {
545 /* No xattrs, nothing to pack */
546 return true;
549 if (fsp == NULL) {
550 DBG_ERR("fsp unexpectedly NULL\n");
551 return false;
554 oldsize = talloc_get_size(ad->ad_data);
555 if (oldsize < AD_XATTR_MAX_HDR_SIZE) {
556 ad->ad_data = talloc_realloc(ad,
557 ad->ad_data,
558 char,
559 AD_XATTR_MAX_HDR_SIZE);
560 if (ad->ad_data == NULL) {
561 return false;
563 memset(ad->ad_data + oldsize,
565 AD_XATTR_MAX_HDR_SIZE - oldsize);
569 * First, let's calculate the start of the xattr data area which will be
570 * after the xattr header + header entries.
573 data_off = ad_getentryoff(ad, ADEID_FINDERI);
574 data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
575 /* 2 bytes padding */
576 data_off += 2;
578 for (i = 0; i < h->adx_num_attrs; i++) {
579 struct ad_xattr_entry *e = &ad->adx_entries[i];
581 /* Align on 4 byte boundary */
582 data_off = (data_off + 3) & ~3;
584 data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
585 if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
586 return false;
590 off = ad_getentryoff(ad, ADEID_FINDERI);
591 off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
592 /* 2 bytes padding */
593 off += 2;
595 for (i = 0; i < h->adx_num_attrs; i++) {
596 struct ad_xattr_entry *e = &ad->adx_entries[i];
598 /* Align on 4 byte boundary */
599 off = (off + 3) & ~3;
601 e->adx_offset = data_off;
602 data_off += e->adx_length;
604 DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
605 "adx_data_off [%zu]\n",
606 (size_t)i,
607 e->adx_name,
608 (size_t)e->adx_namelen,
609 (size_t)off,
610 (size_t)e->adx_length,
611 (size_t)e->adx_offset);
613 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
614 return false;
616 RSIVAL(ad->ad_data, off, e->adx_offset);
617 off += 4;
619 if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
620 return false;
622 RSIVAL(ad->ad_data, off, e->adx_length);
623 off += 4;
625 if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
626 return false;
628 RSSVAL(ad->ad_data, off, e->adx_flags);
629 off += 2;
631 if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
632 return false;
634 SCVAL(ad->ad_data, off, e->adx_namelen);
635 off += 1;
637 if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
638 return false;
640 memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
641 off += e->adx_namelen;
644 h->adx_data_start = off;
645 h->adx_data_length = talloc_get_size(ad->adx_data);
646 h->adx_total_size = h->adx_data_start + h->adx_data_length;
648 if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
649 ad->ad_data = talloc_realloc(ad,
650 ad->ad_data,
651 char,
652 h->adx_total_size);
653 if (ad->ad_data == NULL) {
654 return false;
658 memcpy(ad->ad_data + h->adx_data_start,
659 ad->adx_data,
660 h->adx_data_length);
662 ad_setentrylen(ad,
663 ADEID_FINDERI,
664 h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
666 ad_setentryoff(ad,
667 ADEID_RFORK,
668 ad_getentryoff(ad, ADEID_FINDERI) +
669 ad_getentrylen(ad, ADEID_FINDERI));
671 memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
674 * Rewind, then update the header fields.
677 off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
678 /* 2 bytes padding */
679 off += 2;
681 RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
682 off += 4;
683 RSIVAL(ad->ad_data, off, 0);
684 off += 4;
685 RSIVAL(ad->ad_data, off, h->adx_total_size);
686 off += 4;
687 RSIVAL(ad->ad_data, off, h->adx_data_start);
688 off += 4;
689 RSIVAL(ad->ad_data, off, h->adx_data_length);
690 off += 4;
692 /* adx_reserved and adx_flags */
693 memset(ad->ad_data + off, 0, 3 * 4 + 2);
694 off += 3 * 4 + 2;
696 RSSVAL(ad->ad_data, off, h->adx_num_attrs);
697 off += 2;
699 ok = ad_pack_move_reso(handle, ad, fsp);
700 if (!ok) {
701 DBG_ERR("Moving resourcefork of [%s] failed\n",
702 fsp_str_dbg(fsp));
703 return false;
706 return true;
710 * Pack AppleDouble structure into data buffer
712 static bool ad_pack(struct vfs_handle_struct *handle,
713 struct adouble *ad,
714 files_struct *fsp)
716 uint32_t eid;
717 uint16_t nent;
718 uint32_t bufsize;
719 uint32_t offset = 0;
720 bool ok;
722 bufsize = talloc_get_size(ad->ad_data);
723 if (bufsize < AD_DATASZ_DOT_UND) {
724 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
725 return false;
728 if (offset + ADEDLEN_MAGIC < offset ||
729 offset + ADEDLEN_MAGIC >= bufsize) {
730 return false;
732 RSIVAL(ad->ad_data, offset, ad->ad_magic);
733 offset += ADEDLEN_MAGIC;
735 if (offset + ADEDLEN_VERSION < offset ||
736 offset + ADEDLEN_VERSION >= bufsize) {
737 return false;
739 RSIVAL(ad->ad_data, offset, ad->ad_version);
740 offset += ADEDLEN_VERSION;
742 if (offset + ADEDLEN_FILLER < offset ||
743 offset + ADEDLEN_FILLER >= bufsize) {
744 return false;
746 if (ad->ad_type == ADOUBLE_RSRC) {
747 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
749 offset += ADEDLEN_FILLER;
751 if (offset + ADEDLEN_NENTRIES < offset ||
752 offset + ADEDLEN_NENTRIES >= bufsize) {
753 return false;
755 offset += ADEDLEN_NENTRIES;
757 ok = ad_pack_xattrs(handle, ad, fsp);
758 if (!ok) {
759 return false;
762 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
763 if (ad->ad_eid[eid].ade_off == 0) {
765 * ade_off is also used as indicator whether a
766 * specific entry is used or not
768 continue;
771 if (offset + AD_ENTRY_LEN_EID < offset ||
772 offset + AD_ENTRY_LEN_EID >= bufsize) {
773 return false;
775 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
776 offset += AD_ENTRY_LEN_EID;
778 if (offset + AD_ENTRY_LEN_OFF < offset ||
779 offset + AD_ENTRY_LEN_OFF >= bufsize) {
780 return false;
782 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
783 offset += AD_ENTRY_LEN_OFF;
785 if (offset + AD_ENTRY_LEN_LEN < offset ||
786 offset + AD_ENTRY_LEN_LEN >= bufsize) {
787 return false;
789 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
790 offset += AD_ENTRY_LEN_LEN;
792 nent++;
795 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
796 return false;
798 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
800 return true;
803 static bool ad_unpack_xattrs(struct adouble *ad)
805 struct ad_xattr_header *h = &ad->adx_header;
806 size_t bufsize = talloc_get_size(ad->ad_data);
807 const char *p = ad->ad_data;
808 uint32_t hoff;
809 uint32_t i;
811 if (ad->ad_type != ADOUBLE_RSRC) {
812 return false;
815 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
816 return true;
820 * Ensure the buffer ad->ad_data was allocated by ad_alloc() for an
821 * ADOUBLE_RSRC type (._ AppleDouble file on-disk).
823 if (bufsize != AD_XATTR_MAX_HDR_SIZE) {
824 return false;
827 /* 2 bytes padding */
828 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
830 h->adx_magic = RIVAL(p, hoff + 0);
831 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
832 h->adx_total_size = RIVAL(p, hoff + 8);
833 h->adx_data_start = RIVAL(p, hoff + 12);
834 h->adx_data_length = RIVAL(p, hoff + 16);
835 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
836 h->adx_num_attrs = RSVAL(p, hoff + 34);
838 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
839 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
840 return false;
843 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
844 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
845 return false;
847 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
848 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
849 return false;
852 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
853 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
854 return false;
857 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
858 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
859 return false;
861 if ((h->adx_data_start + h->adx_data_length) >
862 ad->adx_header.adx_total_size)
864 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
865 return false;
868 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
869 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
870 return false;
873 if (h->adx_num_attrs == 0) {
874 return true;
877 ad->adx_entries = talloc_zero_array(
878 ad, struct ad_xattr_entry, h->adx_num_attrs);
879 if (ad->adx_entries == NULL) {
880 return false;
883 hoff += AD_XATTR_HDR_SIZE;
885 for (i = 0; i < h->adx_num_attrs; i++) {
886 struct ad_xattr_entry *e = &ad->adx_entries[i];
888 hoff = (hoff + 3) & ~3;
890 e->adx_offset = RIVAL(p, hoff + 0);
891 e->adx_length = RIVAL(p, hoff + 4);
892 e->adx_flags = RSVAL(p, hoff + 8);
893 e->adx_namelen = *(p + hoff + 10);
895 if (e->adx_offset >= ad->adx_header.adx_total_size) {
896 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
897 e->adx_offset);
898 return false;
901 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
902 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
903 e->adx_length);
904 return false;
907 if ((e->adx_offset + e->adx_length) >
908 ad->adx_header.adx_total_size)
910 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
911 e->adx_length);
912 return false;
915 if (e->adx_namelen == 0) {
916 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
917 e->adx_namelen);
918 return false;
920 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
921 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
922 e->adx_namelen);
923 return false;
925 if ((hoff + 11 + e->adx_namelen) >
926 ad->adx_header.adx_data_start)
928 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
929 e->adx_namelen);
930 return false;
933 e->adx_name = talloc_strndup(ad->adx_entries,
934 p + hoff + 11,
935 e->adx_namelen);
936 if (e->adx_name == NULL) {
937 return false;
940 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
941 e->adx_name, e->adx_offset, e->adx_length);
942 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
943 e->adx_length);
945 hoff += 11 + e->adx_namelen;
948 return true;
952 * Unpack an AppleDouble blob into a struct adoble
954 static bool ad_unpack(struct adouble *ad, const size_t nentries,
955 size_t filesize)
957 size_t bufsize = talloc_get_size(ad->ad_data);
958 size_t adentries, i;
959 uint32_t eid, len, off;
960 bool ok;
963 * The size of the buffer ad->ad_data is checked when read, so
964 * we wouldn't have to check our own offsets, a few extra
965 * checks won't hurt though. We have to check the offsets we
966 * read from the buffer anyway.
969 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
970 DBG_NOTICE("Bad size\n");
971 return false;
974 ad->ad_magic = RIVAL(ad->ad_data, 0);
975 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
976 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
977 DBG_NOTICE("Wrong magic or version\n");
978 return false;
981 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
983 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
984 if (adentries != nentries) {
985 DBG_NOTICE("Invalid number of entries: %zu\n", adentries);
986 return false;
989 /* now, read in the entry bits */
990 for (i = 0; i < adentries; i++) {
991 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
992 eid = get_eid(eid);
993 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
994 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
996 if (!eid || eid >= ADEID_MAX) {
997 DBG_NOTICE("Bogus eid %d\n", eid);
998 return false;
1002 * All entries other than the resource fork are
1003 * expected to be read into the ad_data buffer, so
1004 * ensure the specified offset is within that bound
1006 if ((off > bufsize) && (eid != ADEID_RFORK)) {
1007 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1008 ", len: %" PRIu32 "\n",
1009 eid,
1010 off,
1011 len);
1012 return false;
1015 ok = ad_entry_check_size(eid, bufsize, off, len);
1016 if (!ok) {
1017 DBG_NOTICE("Bogus eid [%" PRIu32 "] bufsize [%zu] "
1018 "off [%" PRIu32 "] len [%" PRIu32 "]\n",
1019 eid,
1020 bufsize,
1021 off,
1022 len);
1023 return false;
1027 * That would be obviously broken
1029 if (off > filesize) {
1030 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1031 ", len: %" PRIu32 "\n",
1032 eid,
1033 off,
1034 len);
1035 return false;
1039 * Check for any entry that has its end beyond the
1040 * filesize.
1042 if (off + len < off) {
1043 DBG_NOTICE("offset wrap in eid %d: off: %" PRIu32
1044 ", len: %" PRIu32 "\n",
1045 eid,
1046 off,
1047 len);
1048 return false;
1051 if (off + len > filesize) {
1053 * If this is the resource fork entry, we fix
1054 * up the length, for any other entry we bail
1055 * out.
1057 if (eid != ADEID_RFORK) {
1058 DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1059 ", len: %" PRIu32 "\n",
1060 eid,
1061 off,
1062 len);
1063 return false;
1067 * Fixup the resource fork entry by limiting
1068 * the size to entryoffset - filesize.
1070 len = filesize - off;
1071 DBG_NOTICE("Limiting ADEID_RFORK: off: %" PRIu32
1072 ", len: %" PRIu32 "\n",
1073 off,
1074 len);
1077 ad->ad_eid[eid].ade_off = off;
1078 ad->ad_eid[eid].ade_len = len;
1081 if (ad->ad_type == ADOUBLE_RSRC) {
1082 ok = ad_unpack_xattrs(ad);
1083 if (!ok) {
1084 return false;
1088 return true;
1091 static bool ad_convert_move_reso(vfs_handle_struct *handle,
1092 struct adouble *ad,
1093 const struct smb_filename *smb_fname)
1095 char *buf = NULL;
1096 size_t rforklen;
1097 size_t rforkoff;
1098 ssize_t n;
1099 int ret;
1101 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1102 if (rforklen == 0) {
1103 return true;
1106 buf = talloc_size(ad, rforklen);
1107 if (buf == NULL) {
1109 * This allocates a buffer for reading the resource fork data in
1110 * one big swoop. Resource forks won't be larger then, say, 64
1111 * MB, I swear, so just doing the allocation with the talloc
1112 * limit as safeguard seems safe.
1114 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1115 rforklen);
1116 return false;
1119 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1121 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1122 if (n != rforklen) {
1123 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1124 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1125 return false;
1128 rforkoff = ADEDOFF_RFORK_DOT_UND;
1130 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1131 if (n != rforklen) {
1132 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1133 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1134 return false;
1137 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1139 ret = ad_fset(handle, ad, ad->ad_fsp);
1140 if (ret != 0) {
1141 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1142 return false;
1145 return true;
1148 static bool ad_convert_xattr(vfs_handle_struct *handle,
1149 struct adouble *ad,
1150 const struct smb_filename *smb_fname,
1151 const char *catia_mappings,
1152 bool *converted_xattr)
1154 static struct char_mappings **string_replace_cmaps = NULL;
1155 uint16_t i;
1156 int saved_errno = 0;
1157 NTSTATUS status;
1158 int rc;
1159 bool ok;
1161 *converted_xattr = false;
1163 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1164 return true;
1167 if (string_replace_cmaps == NULL) {
1168 const char **mappings = NULL;
1170 mappings = str_list_make_v3_const(
1171 talloc_tos(), catia_mappings, NULL);
1172 if (mappings == NULL) {
1173 return false;
1175 string_replace_cmaps = string_replace_init_map(
1176 handle->conn->sconn, mappings);
1177 TALLOC_FREE(mappings);
1180 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1181 struct ad_xattr_entry *e = &ad->adx_entries[i];
1182 char *mapped_name = NULL;
1183 char *tmp = NULL;
1184 struct smb_filename *stream_name = NULL;
1185 files_struct *fsp = NULL;
1186 ssize_t nwritten;
1188 status = string_replace_allocate(handle->conn,
1189 e->adx_name,
1190 string_replace_cmaps,
1191 talloc_tos(),
1192 &mapped_name,
1193 vfs_translate_to_windows);
1194 if (!NT_STATUS_IS_OK(status) &&
1195 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1197 DBG_ERR("string_replace_allocate failed\n");
1198 ok = false;
1199 goto fail;
1202 tmp = mapped_name;
1203 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1204 TALLOC_FREE(tmp);
1205 if (mapped_name == NULL) {
1206 ok = false;
1207 goto fail;
1210 stream_name = synthetic_smb_fname(talloc_tos(),
1211 smb_fname->base_name,
1212 mapped_name,
1213 NULL,
1214 smb_fname->twrp,
1215 smb_fname->flags);
1216 TALLOC_FREE(mapped_name);
1217 if (stream_name == NULL) {
1218 DBG_ERR("synthetic_smb_fname failed\n");
1219 ok = false;
1220 goto fail;
1223 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1225 status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1226 if (!NT_STATUS_IS_OK(status) &&
1227 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1229 ok = false;
1230 goto fail;
1233 status = SMB_VFS_CREATE_FILE(
1234 handle->conn, /* conn */
1235 NULL, /* req */
1236 NULL, /* dirfsp */
1237 stream_name, /* fname */
1238 FILE_GENERIC_WRITE, /* access_mask */
1239 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /* share_access */
1240 FILE_OPEN_IF, /* create_disposition */
1241 0, /* create_options */
1242 0, /* file_attributes */
1243 INTERNAL_OPEN_ONLY, /* oplock_request */
1244 NULL, /* lease */
1245 0, /* allocation_size */
1246 0, /* private_flags */
1247 NULL, /* sd */
1248 NULL, /* ea_list */
1249 &fsp, /* result */
1250 NULL, /* psbuf */
1251 NULL, NULL); /* create context */
1252 TALLOC_FREE(stream_name);
1253 if (!NT_STATUS_IS_OK(status)) {
1254 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1255 ok = false;
1256 goto fail;
1259 nwritten = SMB_VFS_PWRITE(fsp,
1260 ad->ad_data + e->adx_offset,
1261 e->adx_length,
1263 if (nwritten == -1) {
1264 DBG_ERR("SMB_VFS_PWRITE failed\n");
1265 saved_errno = errno;
1266 close_file_free(NULL, &fsp, ERROR_CLOSE);
1267 errno = saved_errno;
1268 ok = false;
1269 goto fail;
1272 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1273 if (!NT_STATUS_IS_OK(status)) {
1274 ok = false;
1275 goto fail;
1277 fsp = NULL;
1280 ad->adx_header.adx_num_attrs = 0;
1281 TALLOC_FREE(ad->adx_entries);
1283 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1285 rc = ad_fset(handle, ad, ad->ad_fsp);
1286 if (rc != 0) {
1287 DBG_ERR("ad_fset on [%s] failed: %s\n",
1288 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1289 ok = false;
1290 goto fail;
1293 ok = ad_convert_move_reso(handle, ad, smb_fname);
1294 if (!ok) {
1295 goto fail;
1298 *converted_xattr = true;
1299 ok = true;
1301 fail:
1302 return ok;
1305 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1306 struct adouble *ad,
1307 const struct smb_filename *smb_fname)
1309 char *p_ad = NULL;
1310 AfpInfo *ai = NULL;
1311 DATA_BLOB aiblob;
1312 struct smb_filename *stream_name = NULL;
1313 files_struct *fsp = NULL;
1314 size_t size;
1315 ssize_t nwritten;
1316 NTSTATUS status;
1317 int saved_errno = 0;
1318 int cmp;
1320 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1321 if (cmp != 0) {
1322 return true;
1325 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1326 if (p_ad == NULL) {
1327 return false;
1330 ai = afpinfo_new(talloc_tos());
1331 if (ai == NULL) {
1332 return false;
1335 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1337 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1338 if (aiblob.data == NULL) {
1339 TALLOC_FREE(ai);
1340 return false;
1343 size = afpinfo_pack(ai, (char *)aiblob.data);
1344 TALLOC_FREE(ai);
1345 if (size != AFP_INFO_SIZE) {
1346 return false;
1349 stream_name = synthetic_smb_fname(talloc_tos(),
1350 smb_fname->base_name,
1351 AFPINFO_STREAM,
1352 NULL,
1353 smb_fname->twrp,
1354 smb_fname->flags);
1355 if (stream_name == NULL) {
1356 data_blob_free(&aiblob);
1357 DBG_ERR("synthetic_smb_fname failed\n");
1358 return false;
1361 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1363 status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1364 if (!NT_STATUS_IS_OK(status) &&
1365 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1367 return false;
1370 status = SMB_VFS_CREATE_FILE(
1371 handle->conn, /* conn */
1372 NULL, /* req */
1373 NULL, /* dirfsp */
1374 stream_name, /* fname */
1375 FILE_GENERIC_WRITE, /* access_mask */
1376 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1377 FILE_OPEN_IF, /* create_disposition */
1378 0, /* create_options */
1379 0, /* file_attributes */
1380 INTERNAL_OPEN_ONLY, /* oplock_request */
1381 NULL, /* lease */
1382 0, /* allocation_size */
1383 0, /* private_flags */
1384 NULL, /* sd */
1385 NULL, /* ea_list */
1386 &fsp, /* result */
1387 NULL, /* psbuf */
1388 NULL, NULL); /* create context */
1389 TALLOC_FREE(stream_name);
1390 if (!NT_STATUS_IS_OK(status)) {
1391 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1392 return false;
1395 nwritten = SMB_VFS_PWRITE(fsp,
1396 aiblob.data,
1397 aiblob.length,
1399 if (nwritten == -1) {
1400 DBG_ERR("SMB_VFS_PWRITE failed\n");
1401 saved_errno = errno;
1402 close_file_free(NULL, &fsp, ERROR_CLOSE);
1403 errno = saved_errno;
1404 return false;
1407 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1408 if (!NT_STATUS_IS_OK(status)) {
1409 return false;
1411 fsp = NULL;
1413 return true;
1416 static bool ad_convert_truncate(vfs_handle_struct *handle,
1417 struct adouble *ad,
1418 const struct smb_filename *smb_fname)
1420 int rc;
1421 off_t newlen;
1423 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1425 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1426 if (rc != 0) {
1427 return false;
1430 return true;
1433 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1434 struct adouble *ad,
1435 uint32_t flags,
1436 bool *blank)
1438 size_t rforklen = sizeof(empty_resourcefork);
1439 char buf[rforklen];
1440 ssize_t nread;
1441 int cmp;
1442 int rc;
1444 *blank = false;
1446 if (!(flags & AD_CONV_WIPE_BLANK)) {
1447 return true;
1450 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1451 return true;
1454 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1455 if (nread != rforklen) {
1456 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1457 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1458 return false;
1461 cmp = memcmp(buf, empty_resourcefork, rforklen);
1462 if (cmp != 0) {
1463 return true;
1466 ad_setentrylen(ad, ADEID_RFORK, 0);
1468 rc = ad_fset(handle, ad, ad->ad_fsp);
1469 if (rc != 0) {
1470 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1471 return false;
1474 *blank = true;
1475 return true;
1478 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1479 struct adouble *ad,
1480 const struct smb_filename *smb_fname,
1481 uint32_t flags)
1483 struct smb_filename *parent_fname = NULL;
1484 struct smb_filename *at_fname = NULL;
1485 struct smb_filename *ad_name = NULL;
1486 NTSTATUS status;
1487 int rc;
1489 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1490 return true;
1493 if (!(flags & AD_CONV_DELETE)) {
1494 return true;
1497 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1498 if (rc != 0) {
1499 return false;
1502 status = parent_pathref(talloc_tos(),
1503 handle->conn->cwd_fsp,
1504 ad_name,
1505 &parent_fname,
1506 &at_fname);
1507 TALLOC_FREE(ad_name);
1508 if (!NT_STATUS_IS_OK(status)) {
1509 return false;
1512 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1513 parent_fname->fsp,
1514 at_fname,
1516 if (rc != 0) {
1517 DBG_ERR("Unlinking [%s/%s] failed: %s\n",
1518 smb_fname_str_dbg(parent_fname),
1519 smb_fname_str_dbg(at_fname), strerror(errno));
1520 TALLOC_FREE(parent_fname);
1521 return false;
1524 DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1525 smb_fname_str_dbg(parent_fname),
1526 smb_fname_str_dbg(at_fname));
1527 TALLOC_FREE(parent_fname);
1529 return true;
1533 * Convert from Apple's ._ file to Netatalk
1535 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1536 * bytes containing packed xattrs.
1538 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1539 * otherwise
1541 int ad_convert(struct vfs_handle_struct *handle,
1542 const struct smb_filename *smb_fname,
1543 const char *catia_mappings,
1544 uint32_t flags)
1546 struct adouble *ad = NULL;
1547 bool ok;
1548 bool converted_xattr = false;
1549 bool blank;
1550 int ret;
1552 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1553 if (ad == NULL) {
1554 return 0;
1557 ok = ad_convert_xattr(handle,
1559 smb_fname,
1560 catia_mappings,
1561 &converted_xattr);
1562 if (!ok) {
1563 ret = -1;
1564 goto done;
1567 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1568 if (!ok) {
1569 ret = -1;
1570 goto done;
1573 if (converted_xattr || blank) {
1574 ok = ad_convert_truncate(handle, ad, smb_fname);
1575 if (!ok) {
1576 ret = -1;
1577 goto done;
1581 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1582 if (!ok) {
1583 DBG_ERR("Failed to convert [%s]\n",
1584 smb_fname_str_dbg(smb_fname));
1585 ret = -1;
1586 goto done;
1589 ok = ad_convert_delete_adfile(handle,
1591 smb_fname,
1592 flags);
1593 if (!ok) {
1594 ret = -1;
1595 goto done;
1598 ret = 0;
1599 done:
1600 TALLOC_FREE(ad);
1601 return ret;
1604 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1605 struct vfs_handle_struct *handle,
1606 struct smb_filename *smb_fname,
1607 struct smb_filename *adpath,
1608 files_struct **_fsp)
1610 files_struct *fsp = NULL;
1611 NTSTATUS status;
1612 int ret;
1614 status = openat_pathref_fsp(handle->conn->cwd_fsp, adpath);
1615 if (!NT_STATUS_IS_OK(status) &&
1616 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1617 return false;
1620 status = SMB_VFS_CREATE_FILE(
1621 handle->conn,
1622 NULL, /* req */
1623 NULL, /* dirfsp */
1624 adpath,
1625 FILE_READ_DATA|FILE_WRITE_DATA,
1626 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1627 FILE_OPEN_IF,
1628 0, /* create_options */
1629 0, /* file_attributes */
1630 INTERNAL_OPEN_ONLY,
1631 NULL, /* lease */
1632 0, /* allocation_size */
1633 0, /* private_flags */
1634 NULL, /* sd */
1635 NULL, /* ea_list */
1636 &fsp,
1637 NULL, /* info */
1638 NULL, NULL); /* create context */
1639 if (!NT_STATUS_IS_OK(status)) {
1640 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1641 smb_fname_str_dbg(adpath), nt_errstr(status));
1642 return false;
1645 if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1646 fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1648 ret = SMB_VFS_FCHOWN(fsp,
1649 smb_fname->st.st_ex_uid,
1650 smb_fname->st.st_ex_gid);
1651 if (ret != 0) {
1652 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1653 fsp_str_dbg(fsp), nt_errstr(status));
1654 close_file_free(NULL, &fsp, NORMAL_CLOSE);
1655 return false;
1659 *_fsp = fsp;
1660 return true;
1663 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1664 struct smb_filename *smb_fname,
1665 TALLOC_CTX *mem_ctx,
1666 unsigned int *num_streams,
1667 struct stream_struct **streams)
1669 files_struct *fsp = NULL;
1670 NTSTATUS status;
1672 status = openat_pathref_fsp(handle->conn->cwd_fsp, smb_fname);
1673 if (!NT_STATUS_IS_OK(status)) {
1674 return false;
1677 status = SMB_VFS_CREATE_FILE(
1678 handle->conn, /* conn */
1679 NULL, /* req */
1680 NULL, /* dirfsp */
1681 smb_fname, /* fname */
1682 FILE_READ_ATTRIBUTES, /* access_mask */
1683 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1684 FILE_SHARE_DELETE),
1685 FILE_OPEN, /* create_disposition*/
1686 0, /* create_options */
1687 0, /* file_attributes */
1688 INTERNAL_OPEN_ONLY, /* oplock_request */
1689 NULL, /* lease */
1690 0, /* allocation_size */
1691 0, /* private_flags */
1692 NULL, /* sd */
1693 NULL, /* ea_list */
1694 &fsp, /* result */
1695 NULL, /* pinfo */
1696 NULL, NULL); /* create context */
1697 if (!NT_STATUS_IS_OK(status)) {
1698 DBG_ERR("Opening [%s] failed: %s\n",
1699 smb_fname_str_dbg(smb_fname),
1700 nt_errstr(status));
1701 return false;
1704 status = vfs_fstreaminfo(fsp,
1705 mem_ctx,
1706 num_streams,
1707 streams);
1708 if (!NT_STATUS_IS_OK(status)) {
1709 close_file_free(NULL, &fsp, NORMAL_CLOSE);
1710 DBG_ERR("streaminfo on [%s] failed: %s\n",
1711 smb_fname_str_dbg(smb_fname),
1712 nt_errstr(status));
1713 return false;
1716 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1717 if (!NT_STATUS_IS_OK(status)) {
1718 DBG_ERR("close_file [%s] failed: %s\n",
1719 smb_fname_str_dbg(smb_fname),
1720 nt_errstr(status));
1721 return false;
1724 return true;
1727 struct ad_collect_state {
1728 bool have_adfile;
1729 size_t adx_data_off;
1730 char *rsrc_data_buf;
1733 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1734 struct char_mappings **cmaps,
1735 struct smb_filename *smb_fname,
1736 const struct stream_struct *stream,
1737 struct adouble *ad,
1738 struct ad_collect_state *state)
1740 struct smb_filename *sname = NULL;
1741 files_struct *fsp = NULL;
1742 struct ad_xattr_entry *e = NULL;
1743 char *mapped_name = NULL;
1744 char *p = NULL;
1745 size_t needed_size;
1746 ssize_t nread;
1747 NTSTATUS status;
1748 bool ok;
1750 sname = synthetic_smb_fname(ad,
1751 smb_fname->base_name,
1752 stream->name,
1753 NULL,
1754 smb_fname->twrp,
1756 if (sname == NULL) {
1757 return false;
1760 if (is_ntfs_default_stream_smb_fname(sname)) {
1761 TALLOC_FREE(sname);
1762 return true;
1765 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1767 status = openat_pathref_fsp(handle->conn->cwd_fsp, sname);
1768 if (!NT_STATUS_IS_OK(status)) {
1769 ok = false;
1770 goto out;
1773 status = SMB_VFS_CREATE_FILE(
1774 handle->conn,
1775 NULL, /* req */
1776 NULL, /* dirfsp */
1777 sname,
1778 FILE_READ_DATA|DELETE_ACCESS,
1779 FILE_SHARE_READ,
1780 FILE_OPEN,
1781 0, /* create_options */
1782 0, /* file_attributes */
1783 INTERNAL_OPEN_ONLY, /* oplock_request */
1784 NULL, /* lease */
1785 0, /* allocation_size */
1786 0, /* private_flags */
1787 NULL, /* sd */
1788 NULL, /* ea_list */
1789 &fsp,
1790 NULL, /* info */
1791 NULL, NULL); /* create context */
1792 if (!NT_STATUS_IS_OK(status)) {
1793 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1794 smb_fname_str_dbg(sname));
1795 ok = false;
1796 goto out;
1799 if (is_afpinfo_stream(stream->name)) {
1800 char buf[AFP_INFO_SIZE];
1802 if (stream->size != AFP_INFO_SIZE) {
1803 DBG_ERR("Bad size [%zd] on [%s]\n",
1804 (ssize_t)stream->size,
1805 smb_fname_str_dbg(sname));
1806 ok = false;
1807 goto out;
1810 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1811 if (nread != AFP_INFO_SIZE) {
1812 DBG_ERR("Bad size [%zd] on [%s]\n",
1813 (ssize_t)stream->size,
1814 smb_fname_str_dbg(sname));
1815 ok = false;
1816 goto out;
1819 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1820 buf + AFP_OFF_FinderInfo,
1821 AFP_FinderSize);
1823 ok = set_delete_on_close(fsp,
1824 true,
1825 fsp->conn->session_info->security_token,
1826 fsp->conn->session_info->unix_token);
1827 if (!ok) {
1828 DBG_ERR("Deleting [%s] failed\n",
1829 smb_fname_str_dbg(sname));
1830 ok = false;
1831 goto out;
1833 ok = true;
1834 goto out;
1837 if (is_afpresource_stream(stream->name)) {
1838 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1839 if (ad->ad_rsrc_data == NULL) {
1840 ok = false;
1841 goto out;
1844 nread = SMB_VFS_PREAD(fsp,
1845 ad->ad_rsrc_data,
1846 stream->size,
1848 if (nread != stream->size) {
1849 DBG_ERR("Bad size [%zd] on [%s]\n",
1850 (ssize_t)stream->size,
1851 smb_fname_str_dbg(sname));
1852 ok = false;
1853 goto out;
1856 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1858 if (!state->have_adfile) {
1860 * We have a resource *stream* but no AppleDouble
1861 * sidecar file, this means the share is configured with
1862 * fruit:resource=stream. So we should delete the
1863 * resource stream.
1865 ok = set_delete_on_close(
1866 fsp,
1867 true,
1868 fsp->conn->session_info->security_token,
1869 fsp->conn->session_info->unix_token);
1870 if (!ok) {
1871 DBG_ERR("Deleting [%s] failed\n",
1872 smb_fname_str_dbg(sname));
1873 ok = false;
1874 goto out;
1877 ok = true;
1878 goto out;
1881 ad->adx_entries = talloc_realloc(ad,
1882 ad->adx_entries,
1883 struct ad_xattr_entry,
1884 ad->adx_header.adx_num_attrs + 1);
1885 if (ad->adx_entries == NULL) {
1886 ok = false;
1887 goto out;
1890 e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1891 *e = (struct ad_xattr_entry) {
1892 .adx_length = stream->size,
1894 e->adx_name = talloc_strdup(ad, stream->name + 1);
1895 if (e->adx_name == NULL) {
1896 ok = false;
1897 goto out;
1899 p = strchr(e->adx_name, ':');
1900 if (p != NULL) {
1901 *p = '\0';
1904 status = string_replace_allocate(handle->conn,
1905 e->adx_name,
1906 cmaps,
1908 &mapped_name,
1909 vfs_translate_to_unix);
1910 if (!NT_STATUS_IS_OK(status) &&
1911 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1913 DBG_ERR("string_replace_allocate failed\n");
1914 ok = false;
1915 goto out;
1918 e->adx_name = mapped_name;
1919 e->adx_namelen = strlen(e->adx_name) + 1,
1921 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1922 ad->adx_header.adx_num_attrs,
1923 e->adx_name,
1924 (size_t)e->adx_length);
1926 ad->adx_header.adx_num_attrs++;
1928 needed_size = state->adx_data_off + stream->size;
1929 if (needed_size > talloc_get_size(ad->adx_data)) {
1930 ad->adx_data = talloc_realloc(ad,
1931 ad->adx_data,
1932 char,
1933 needed_size);
1934 if (ad->adx_data == NULL) {
1935 ok = false;
1936 goto out;
1940 nread = SMB_VFS_PREAD(fsp,
1941 ad->adx_data + state->adx_data_off,
1942 stream->size,
1944 if (nread != stream->size) {
1945 DBG_ERR("Bad size [%zd] on [%s]\n",
1946 (ssize_t)stream->size,
1947 smb_fname_str_dbg(sname));
1948 ok = false;
1949 goto out;
1951 state->adx_data_off += nread;
1953 ok = set_delete_on_close(fsp,
1954 true,
1955 fsp->conn->session_info->security_token,
1956 fsp->conn->session_info->unix_token);
1957 if (!ok) {
1958 DBG_ERR("Deleting [%s] failed\n",
1959 smb_fname_str_dbg(sname));
1960 ok = false;
1961 goto out;
1964 out:
1965 TALLOC_FREE(sname);
1966 if (fsp != NULL) {
1967 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1968 if (!NT_STATUS_IS_OK(status)) {
1969 DBG_ERR("close_file [%s] failed: %s\n",
1970 smb_fname_str_dbg(smb_fname),
1971 nt_errstr(status));
1972 ok = false;
1976 return ok;
1980 * Convert filesystem metadata to AppleDouble file
1982 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1983 struct vfs_handle_struct *handle,
1984 const char *catia_mappings,
1985 struct smb_filename *smb_fname,
1986 bool *converted)
1988 static struct char_mappings **cmaps = NULL;
1989 TALLOC_CTX *frame = talloc_stackframe();
1990 struct ad_collect_state state;
1991 struct stream_struct *streams = NULL;
1992 struct smb_filename *adpath = NULL;
1993 struct adouble *ad = NULL;
1994 unsigned int num_streams = 0;
1995 size_t to_convert = 0;
1996 bool have_rsrc = false;
1997 files_struct *fsp = NULL;
1998 size_t i;
1999 NTSTATUS status;
2000 int ret;
2001 bool ok;
2003 *converted = false;
2005 if (cmaps == NULL) {
2006 const char **mappings = NULL;
2008 mappings = str_list_make_v3_const(
2009 frame, catia_mappings, NULL);
2010 if (mappings == NULL) {
2011 ok = false;
2012 goto out;
2014 cmaps = string_replace_init_map(mem_ctx, mappings);
2015 TALLOC_FREE(mappings);
2018 ok = ad_unconvert_get_streams(handle,
2019 smb_fname,
2020 frame,
2021 &num_streams,
2022 &streams);
2023 if (!ok) {
2024 goto out;
2027 for (i = 0; i < num_streams; i++) {
2028 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
2029 continue;
2031 to_convert++;
2032 if (is_afpresource_stream(streams[i].name)) {
2033 have_rsrc = true;
2037 if (to_convert == 0) {
2038 ok = true;
2039 goto out;
2042 state = (struct ad_collect_state) {
2043 .adx_data_off = 0,
2046 ret = adouble_path(frame, smb_fname, &adpath);
2047 if (ret != 0) {
2048 ok = false;
2049 goto out;
2052 ret = SMB_VFS_STAT(handle->conn, adpath);
2053 if (ret == 0) {
2054 state.have_adfile = true;
2055 } else {
2056 if (errno != ENOENT) {
2057 ok = false;
2058 goto out;
2060 state.have_adfile = false;
2063 if (to_convert == 1 && have_rsrc && state.have_adfile) {
2065 * So we have just a single stream, the resource fork stream
2066 * from an AppleDouble file. Fine, that means there's nothing to
2067 * convert.
2069 ok = true;
2070 goto out;
2073 ad = ad_init(frame, ADOUBLE_RSRC);
2074 if (ad == NULL) {
2075 ok = false;
2076 goto out;
2079 for (i = 0; i < num_streams; i++) {
2080 ok = ad_collect_one_stream(handle,
2081 cmaps,
2082 smb_fname,
2083 &streams[i],
2085 &state);
2086 if (!ok) {
2087 goto out;
2091 ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
2092 if (!ok) {
2093 DBG_ERR("Failed to open adfile [%s]\n",
2094 smb_fname_str_dbg(smb_fname));
2095 goto out;
2098 ret = ad_fset(handle, ad, fsp);
2099 if (ret != 0) {
2100 ok = false;
2101 goto out;
2104 *converted = true;
2105 ok = true;
2107 out:
2108 if (fsp != NULL) {
2109 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
2110 if (!NT_STATUS_IS_OK(status)) {
2111 DBG_ERR("close_file_free() [%s] failed: %s\n",
2112 smb_fname_str_dbg(smb_fname),
2113 nt_errstr(status));
2114 ok = false;
2117 TALLOC_FREE(frame);
2118 return ok;
2122 * Read and parse Netatalk AppleDouble metadata xattr
2124 static ssize_t ad_read_meta(vfs_handle_struct *handle,
2125 struct adouble *ad,
2126 const struct smb_filename *smb_fname)
2128 int rc = 0;
2129 ssize_t ealen;
2130 bool ok;
2131 struct files_struct *fsp = smb_fname->fsp;
2133 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
2135 fsp = metadata_fsp(fsp);
2137 ealen = SMB_VFS_FGETXATTR(fsp,
2138 AFPINFO_EA_NETATALK,
2139 ad->ad_data,
2140 AD_DATASZ_XATTR);
2142 if (ealen == -1) {
2143 switch (errno) {
2144 case ENOATTR:
2145 case ENOENT:
2146 if (errno == ENOATTR) {
2147 errno = ENOENT;
2149 rc = -1;
2150 goto exit;
2151 default:
2152 DEBUG(2, ("error reading meta xattr: %s\n",
2153 strerror(errno)));
2154 rc = -1;
2155 goto exit;
2158 if (ealen != AD_DATASZ_XATTR) {
2159 DEBUG(2, ("bad size %zd\n", ealen));
2160 errno = EINVAL;
2161 rc = -1;
2162 goto exit;
2165 /* Now parse entries */
2166 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2167 if (!ok) {
2168 DBG_WARNING(
2169 "Invalid AppleDouble xattr metadata (%s) in file: %s - "
2170 "Consider deleting the corrupted file.\n",
2171 smb_fname->base_name,
2172 ad->ad_fsp->fsp_name->base_name);
2173 errno = EINVAL;
2174 rc = -1;
2175 goto exit;
2178 if (!ad_getentryoff(ad, ADEID_FINDERI)
2179 || !ad_getentryoff(ad, ADEID_COMMENT)
2180 || !ad_getentryoff(ad, ADEID_FILEDATESI)
2181 || !ad_getentryoff(ad, ADEID_AFPFILEI)
2182 || !ad_getentryoff(ad, ADEID_PRIVDEV)
2183 || !ad_getentryoff(ad, ADEID_PRIVINO)
2184 || !ad_getentryoff(ad, ADEID_PRIVSYN)
2185 || !ad_getentryoff(ad, ADEID_PRIVID)) {
2186 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2187 errno = EINVAL;
2188 rc = -1;
2189 goto exit;
2192 exit:
2193 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2194 smb_fname->base_name, rc));
2196 if (rc != 0) {
2197 ealen = -1;
2198 if (errno == EINVAL) {
2199 become_root();
2200 (void)SMB_VFS_FREMOVEXATTR(fsp,
2201 AFPINFO_EA_NETATALK);
2202 unbecome_root();
2203 errno = ENOENT;
2206 return ealen;
2209 static NTSTATUS adouble_open_rsrc_fsp(const struct files_struct *dirfsp,
2210 const struct smb_filename *smb_base_fname,
2211 int in_flags,
2212 mode_t mode,
2213 struct files_struct **_ad_fsp)
2215 int rc = 0;
2216 struct adouble *ad = NULL;
2217 struct smb_filename *adp_smb_fname = NULL;
2218 struct files_struct *ad_fsp = NULL;
2219 NTSTATUS status;
2220 struct vfs_open_how how = { .flags = in_flags, .mode = mode, };
2222 rc = adouble_path(talloc_tos(),
2223 smb_base_fname,
2224 &adp_smb_fname);
2225 if (rc != 0) {
2226 return NT_STATUS_NO_MEMORY;
2229 status = create_internal_fsp(dirfsp->conn,
2230 adp_smb_fname,
2231 &ad_fsp);
2232 if (!NT_STATUS_IS_OK(status)) {
2233 return status;
2236 #ifdef O_PATH
2237 how.flags &= ~(O_PATH);
2238 #endif
2239 if (how.flags & (O_CREAT | O_TRUNC | O_WRONLY)) {
2240 /* We always need read/write access for the metadata header too */
2241 how.flags &= ~(O_WRONLY);
2242 how.flags |= O_RDWR;
2245 status = fd_openat(dirfsp,
2246 adp_smb_fname,
2247 ad_fsp,
2248 &how);
2249 if (!NT_STATUS_IS_OK(status)) {
2250 file_free(NULL, ad_fsp);
2251 return status;
2254 if (how.flags & (O_CREAT | O_TRUNC)) {
2255 ad = ad_init(talloc_tos(), ADOUBLE_RSRC);
2256 if (ad == NULL) {
2257 file_free(NULL, ad_fsp);
2258 return NT_STATUS_NO_MEMORY;
2261 rc = ad_fset(ad_fsp->conn->vfs_handles, ad, ad_fsp);
2262 if (rc != 0) {
2263 file_free(NULL, ad_fsp);
2264 return NT_STATUS_IO_DEVICE_ERROR;
2266 TALLOC_FREE(ad);
2269 *_ad_fsp = ad_fsp;
2270 return NT_STATUS_OK;
2273 NTSTATUS adouble_open_from_base_fsp(const struct files_struct *dirfsp,
2274 struct files_struct *base_fsp,
2275 adouble_type_t type,
2276 int flags,
2277 mode_t mode,
2278 struct files_struct **_ad_fsp)
2280 *_ad_fsp = NULL;
2282 SMB_ASSERT(base_fsp != NULL);
2283 SMB_ASSERT(!fsp_is_alternate_stream(base_fsp));
2285 switch (type) {
2286 case ADOUBLE_META:
2287 return NT_STATUS_INTERNAL_ERROR;
2288 case ADOUBLE_RSRC:
2289 return adouble_open_rsrc_fsp(dirfsp,
2290 base_fsp->fsp_name,
2291 flags,
2292 mode,
2293 _ad_fsp);
2296 return NT_STATUS_INTERNAL_ERROR;
2300 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2301 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2302 * for file IO on the ._ file.
2304 static int ad_open(vfs_handle_struct *handle,
2305 struct adouble *ad,
2306 files_struct *fsp,
2307 const struct smb_filename *smb_fname,
2308 int flags,
2309 mode_t mode)
2311 NTSTATUS status;
2313 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2314 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2316 if (ad->ad_type == ADOUBLE_META) {
2317 return 0;
2320 if (fsp != NULL) {
2321 ad->ad_fsp = fsp;
2322 ad->ad_opened = false;
2323 return 0;
2326 status = adouble_open_rsrc_fsp(handle->conn->cwd_fsp,
2327 smb_fname,
2328 flags,
2329 mode,
2330 &ad->ad_fsp);
2331 if (!NT_STATUS_IS_OK(status)) {
2332 errno = map_errno_from_nt_status(status);
2333 return -1;
2335 ad->ad_opened = true;
2337 DBG_DEBUG("Path [%s] type [%s]\n",
2338 smb_fname->base_name,
2339 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2341 return 0;
2344 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2345 struct adouble *ad,
2346 const struct smb_filename *smb_fname)
2348 size_t to_read;
2349 ssize_t len;
2350 int ret;
2351 bool ok;
2353 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2354 if (ret != 0) {
2355 DBG_ERR("fstat [%s] failed: %s\n",
2356 fsp_str_dbg(ad->ad_fsp), strerror(errno));
2357 return -1;
2360 to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2361 if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2362 to_read = AD_XATTR_MAX_HDR_SIZE;
2365 len = SMB_VFS_NEXT_PREAD(handle,
2366 ad->ad_fsp,
2367 ad->ad_data,
2368 to_read,
2370 if (len != to_read) {
2371 DBG_NOTICE("%s %s: bad size: %zd\n",
2372 smb_fname->base_name, strerror(errno), len);
2373 return -1;
2376 /* Now parse entries */
2377 ok = ad_unpack(ad,
2378 ADEID_NUM_DOT_UND,
2379 ad->ad_fsp->fsp_name->st.st_ex_size);
2380 if (!ok) {
2381 DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2382 "Consider deleting the corrupted file.\n",
2383 smb_fname->base_name,
2384 ad->ad_fsp->fsp_name->base_name);
2385 errno = EINVAL;
2386 return -1;
2389 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2390 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2391 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2393 DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2394 "Consider deleting the corrupted file.\n",
2395 smb_fname->base_name,
2396 ad->ad_fsp->fsp_name->base_name);
2397 errno = EINVAL;
2398 return -1;
2401 return len;
2405 * Read and parse resource fork, either ._ AppleDouble file or xattr
2407 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2408 struct adouble *ad,
2409 const struct smb_filename *smb_fname)
2411 return ad_read_rsrc_adouble(handle, ad, smb_fname);
2415 * Read and unpack an AppleDouble metadata xattr or resource
2417 static ssize_t ad_read(vfs_handle_struct *handle,
2418 struct adouble *ad,
2419 const struct smb_filename *smb_fname)
2421 switch (ad->ad_type) {
2422 case ADOUBLE_META:
2423 return ad_read_meta(handle, ad, smb_fname);
2424 case ADOUBLE_RSRC:
2425 return ad_read_rsrc(handle, ad, smb_fname);
2426 default:
2427 return -1;
2431 static int adouble_destructor(struct adouble *ad)
2433 NTSTATUS status;
2435 if (!ad->ad_opened) {
2436 return 0;
2439 SMB_ASSERT(ad->ad_fsp != NULL);
2441 status = fd_close(ad->ad_fsp);
2442 if (!NT_STATUS_IS_OK(status)) {
2443 DBG_ERR("Closing [%s] failed: %s\n",
2444 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2446 file_free(NULL, ad->ad_fsp);
2447 ad->ad_fsp = NULL;
2448 ad->ad_opened = false;
2450 return 0;
2454 * Allocate a struct adouble without initialiing it
2456 * The struct is either hang of the fsp extension context or if fsp is
2457 * NULL from ctx.
2459 * @param[in] ctx talloc context
2460 * @param[in] handle vfs handle
2461 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2463 * @return adouble handle
2465 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2466 adouble_type_t type)
2468 int rc = 0;
2469 size_t adsize = 0;
2470 struct adouble *ad;
2472 switch (type) {
2473 case ADOUBLE_META:
2474 adsize = AD_DATASZ_XATTR;
2475 break;
2476 case ADOUBLE_RSRC:
2478 * AppleDouble ._ file case, optimize for fewer (but larger)
2479 * IOs. Two cases:
2481 * - without xattrs size of the header is exactly
2482 * AD_DATASZ_DOT_UND (82) bytes
2484 * - with embedded xattrs it can be larger, up to
2485 * AD_XATTR_MAX_HDR_SIZE
2487 * Larger headers are not supported, but this is a reasonable
2488 * limit that is also employed by the macOS client.
2490 * We used the largest possible size to be able to read the full
2491 * header with one IO.
2493 adsize = AD_XATTR_MAX_HDR_SIZE;
2494 break;
2495 default:
2496 return NULL;
2499 ad = talloc_zero(ctx, struct adouble);
2500 if (ad == NULL) {
2501 rc = -1;
2502 goto exit;
2505 if (adsize) {
2506 ad->ad_data = talloc_zero_array(ad, char, adsize);
2507 if (ad->ad_data == NULL) {
2508 rc = -1;
2509 goto exit;
2513 ad->ad_type = type;
2514 ad->ad_magic = AD_MAGIC;
2515 ad->ad_version = AD_VERSION;
2517 talloc_set_destructor(ad, adouble_destructor);
2519 exit:
2520 if (rc != 0) {
2521 TALLOC_FREE(ad);
2523 return ad;
2527 * Allocate and initialize a new struct adouble
2529 * @param[in] ctx talloc context
2530 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2532 * @return adouble handle, initialized
2534 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2536 int rc = 0;
2537 const struct ad_entry_order *eid;
2538 struct adouble *ad = NULL;
2539 time_t t = time(NULL);
2541 switch (type) {
2542 case ADOUBLE_META:
2543 eid = entry_order_meta_xattr;
2544 break;
2545 case ADOUBLE_RSRC:
2546 eid = entry_order_dot_und;
2547 break;
2548 default:
2549 return NULL;
2552 ad = ad_alloc(ctx, type);
2553 if (ad == NULL) {
2554 return NULL;
2557 while (eid->id) {
2558 ad->ad_eid[eid->id].ade_off = eid->offset;
2559 ad->ad_eid[eid->id].ade_len = eid->len;
2560 eid++;
2563 /* put something sane in the date fields */
2564 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2565 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2566 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2567 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2569 if (rc != 0) {
2570 TALLOC_FREE(ad);
2572 return ad;
2575 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2576 vfs_handle_struct *handle,
2577 files_struct *fsp,
2578 const struct smb_filename *smb_fname,
2579 adouble_type_t type)
2581 int rc = 0;
2582 ssize_t len;
2583 struct adouble *ad = NULL;
2584 int mode;
2586 if (fsp != NULL) {
2587 if (fsp_is_alternate_stream(fsp)) {
2588 smb_fname = fsp->base_fsp->fsp_name;
2589 } else {
2590 smb_fname = fsp->fsp_name;
2594 DEBUG(10, ("ad_get(%s) called for %s\n",
2595 type == ADOUBLE_META ? "meta" : "rsrc",
2596 smb_fname != NULL ? smb_fname->base_name : "???"));
2598 ad = ad_alloc(ctx, type);
2599 if (ad == NULL) {
2600 rc = -1;
2601 goto exit;
2604 /* Try rw first so we can use the fd in ad_convert() */
2605 mode = O_RDWR;
2607 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2608 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2609 mode = O_RDONLY;
2610 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2612 if (rc == -1) {
2613 DBG_DEBUG("ad_open [%s] error [%s]\n",
2614 smb_fname->base_name, strerror(errno));
2615 goto exit;
2619 len = ad_read(handle, ad, smb_fname);
2620 if (len == -1) {
2621 DEBUG(10, ("error reading AppleDouble for %s\n",
2622 smb_fname->base_name));
2623 rc = -1;
2624 goto exit;
2627 exit:
2628 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2629 type == ADOUBLE_META ? "meta" : "rsrc",
2630 smb_fname->base_name, rc));
2632 if (rc != 0) {
2633 TALLOC_FREE(ad);
2635 return ad;
2639 * Return AppleDouble data for a file
2641 * @param[in] ctx talloc context
2642 * @param[in] handle vfs handle
2643 * @param[in] smb_fname pathname to file or directory
2644 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2646 * @return talloced struct adouble or NULL on error
2648 struct adouble *ad_get(TALLOC_CTX *ctx,
2649 vfs_handle_struct *handle,
2650 const struct smb_filename *smb_fname,
2651 adouble_type_t type)
2653 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2657 * Return AppleDouble data for a file
2659 * @param[in] ctx talloc context
2660 * @param[in] handle vfs handle
2661 * @param[in] fsp fsp to use for IO
2662 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2664 * @return talloced struct adouble or NULL on error
2666 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2667 files_struct *fsp, adouble_type_t type)
2669 return ad_get_internal(ctx, handle, fsp, NULL, type);
2673 * Set AppleDouble metadata on a file or directory
2675 * @param[in] ad adouble handle
2676 * @param[in] fsp file handle
2678 * @return status code, 0 means success
2680 int ad_fset(struct vfs_handle_struct *handle,
2681 struct adouble *ad,
2682 files_struct *fsp)
2684 int rc = -1;
2685 ssize_t len;
2686 bool ok;
2688 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2690 ok = ad_pack(handle, ad, fsp);
2691 if (!ok) {
2692 return -1;
2695 switch (ad->ad_type) {
2696 case ADOUBLE_META:
2697 rc = SMB_VFS_NEXT_FSETXATTR(handle,
2698 fsp->base_fsp ? fsp->base_fsp : fsp,
2699 AFPINFO_EA_NETATALK,
2700 ad->ad_data,
2701 AD_DATASZ_XATTR, 0);
2702 break;
2703 case ADOUBLE_RSRC:
2704 len = SMB_VFS_NEXT_PWRITE(handle,
2705 fsp,
2706 ad->ad_data,
2707 ad_getentryoff(ad, ADEID_RFORK),
2709 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2710 DBG_ERR("short write on %s: %zd\n", fsp_str_dbg(fsp), len);
2711 return -1;
2713 rc = 0;
2714 break;
2716 default:
2717 return -1;
2720 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2722 return rc;
2725 bool is_adouble_file(const char *path)
2727 const char *p = NULL;
2728 int match;
2730 p = strrchr(path, '/');
2731 if (p == NULL) {
2732 p = path;
2733 } else {
2734 p++;
2737 match = strncmp(p,
2738 ADOUBLE_NAME_PREFIX,
2739 strlen(ADOUBLE_NAME_PREFIX));
2740 if (match != 0) {
2741 return false;
2743 return true;
2747 * Prepend "._" to a basename
2748 * Return a new struct smb_filename with stream_name == NULL.
2750 int adouble_path(TALLOC_CTX *ctx,
2751 const struct smb_filename *smb_fname_in,
2752 struct smb_filename **pp_smb_fname_out)
2754 char *parent;
2755 const char *base;
2756 struct smb_filename *smb_fname = NULL;
2758 smb_fname = cp_smb_filename_nostream(ctx, smb_fname_in);
2759 if (smb_fname == NULL) {
2760 return -1;
2763 /* We're replacing base_name. */
2764 TALLOC_FREE(smb_fname->base_name);
2766 SET_STAT_INVALID(smb_fname->st);
2768 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2769 &parent, &base)) {
2770 TALLOC_FREE(smb_fname);
2771 return -1;
2774 if (ISDOT(parent)) {
2775 smb_fname->base_name = talloc_asprintf(smb_fname,
2776 "._%s", base);
2777 } else {
2778 smb_fname->base_name = talloc_asprintf(smb_fname,
2779 "%s/._%s", parent, base);
2781 if (smb_fname->base_name == NULL) {
2782 TALLOC_FREE(smb_fname);
2783 return -1;
2786 *pp_smb_fname_out = smb_fname;
2788 return 0;
2792 * Allocate and initialize an AfpInfo struct
2794 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2796 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2797 if (ai == NULL) {
2798 return NULL;
2800 ai->afpi_Signature = AFP_Signature;
2801 ai->afpi_Version = AFP_Version;
2802 ai->afpi_BackupTime = AD_DATE_START;
2803 return ai;
2807 * Pack an AfpInfo struct into a buffer
2809 * Buffer size must be at least AFP_INFO_SIZE
2810 * Returns size of packed buffer
2812 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2814 memset(buf, 0, AFP_INFO_SIZE);
2816 RSIVAL(buf, 0, ai->afpi_Signature);
2817 RSIVAL(buf, 4, ai->afpi_Version);
2818 RSIVAL(buf, 12, ai->afpi_BackupTime);
2819 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2821 return AFP_INFO_SIZE;
2825 * Unpack a buffer into a AfpInfo structure
2827 * Buffer size must be at least AFP_INFO_SIZE
2828 * Returns allocated AfpInfo struct
2830 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate)
2832 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2833 if (ai == NULL) {
2834 return NULL;
2837 ai->afpi_Signature = RIVAL(data, 0);
2838 ai->afpi_Version = RIVAL(data, 4);
2839 ai->afpi_BackupTime = RIVAL(data, 12);
2840 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2841 sizeof(ai->afpi_FinderInfo));
2843 if (validate) {
2844 if (ai->afpi_Signature != AFP_Signature
2845 || ai->afpi_Version != AFP_Version)
2847 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2848 TALLOC_FREE(ai);
2850 } else {
2851 ai->afpi_Signature = AFP_Signature;
2852 ai->afpi_Version = AFP_Version;
2855 return ai;