s3: tests: Add new test_stream_dir_rename.sh test.
[Samba.git] / source3 / lib / adouble.c
blob062c73ec75889188582e81bc09e2ef3b5fde6373
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 * 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 fo 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 DEBUG(1, ("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 DEBUG(1, ("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 DEBUG(1, ("invalid number of entries: %zu\n",
986 adentries));
987 return false;
990 /* now, read in the entry bits */
991 for (i = 0; i < adentries; i++) {
992 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
993 eid = get_eid(eid);
994 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
995 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
997 if (!eid || eid >= ADEID_MAX) {
998 DEBUG(1, ("bogus eid %d\n", eid));
999 return false;
1003 * All entries other than the resource fork are
1004 * expected to be read into the ad_data buffer, so
1005 * ensure the specified offset is within that bound
1007 if ((off > bufsize) && (eid != ADEID_RFORK)) {
1008 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
1009 eid, off, len));
1010 return false;
1013 ok = ad_entry_check_size(eid, bufsize, off, len);
1014 if (!ok) {
1015 DBG_ERR("bogus eid [%"PRIu32"] bufsize [%zu] "
1016 "off [%"PRIu32"] len [%"PRIu32"]\n",
1017 eid, bufsize, off, len);
1018 return false;
1022 * That would be obviously broken
1024 if (off > filesize) {
1025 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
1026 eid, off, len));
1027 return false;
1031 * Check for any entry that has its end beyond the
1032 * filesize.
1034 if (off + len < off) {
1035 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
1036 ", len: %" PRIu32 "\n",
1037 eid, off, len));
1038 return false;
1041 if (off + len > filesize) {
1043 * If this is the resource fork entry, we fix
1044 * up the length, for any other entry we bail
1045 * out.
1047 if (eid != ADEID_RFORK) {
1048 DEBUG(1, ("bogus eid %d: off: %" PRIu32
1049 ", len: %" PRIu32 "\n",
1050 eid, off, len));
1051 return false;
1055 * Fixup the resource fork entry by limiting
1056 * the size to entryoffset - filesize.
1058 len = filesize - off;
1059 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
1060 ", len: %" PRIu32 "\n", off, len));
1063 ad->ad_eid[eid].ade_off = off;
1064 ad->ad_eid[eid].ade_len = len;
1067 if (ad->ad_type == ADOUBLE_RSRC) {
1068 ok = ad_unpack_xattrs(ad);
1069 if (!ok) {
1070 return false;
1074 return true;
1077 static bool ad_convert_move_reso(vfs_handle_struct *handle,
1078 struct adouble *ad,
1079 const struct smb_filename *smb_fname)
1081 char *buf = NULL;
1082 size_t rforklen;
1083 size_t rforkoff;
1084 ssize_t n;
1085 int ret;
1087 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1088 if (rforklen == 0) {
1089 return true;
1092 buf = talloc_size(ad, rforklen);
1093 if (buf == NULL) {
1095 * This allocates a buffer for reading the resource fork data in
1096 * one big swoop. Resource forks won't be larger then, say, 64
1097 * MB, I swear, so just doing the allocation with the talloc
1098 * limit as safeguard seems safe.
1100 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1101 rforklen);
1102 return false;
1105 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1107 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1108 if (n != rforklen) {
1109 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1110 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1111 return false;
1114 rforkoff = ADEDOFF_RFORK_DOT_UND;
1116 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1117 if (n != rforklen) {
1118 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1119 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1120 return false;
1123 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1125 ret = ad_fset(handle, ad, ad->ad_fsp);
1126 if (ret != 0) {
1127 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1128 return false;
1131 return true;
1134 static bool ad_convert_xattr(vfs_handle_struct *handle,
1135 struct adouble *ad,
1136 const struct smb_filename *smb_fname,
1137 const char *catia_mappings,
1138 bool *converted_xattr)
1140 static struct char_mappings **string_replace_cmaps = NULL;
1141 uint16_t i;
1142 int saved_errno = 0;
1143 NTSTATUS status;
1144 int rc;
1145 bool ok;
1147 *converted_xattr = false;
1149 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1150 return true;
1153 if (string_replace_cmaps == NULL) {
1154 const char **mappings = NULL;
1156 mappings = str_list_make_v3_const(
1157 talloc_tos(), catia_mappings, NULL);
1158 if (mappings == NULL) {
1159 return false;
1161 string_replace_cmaps = string_replace_init_map(
1162 handle->conn->sconn, mappings);
1163 TALLOC_FREE(mappings);
1166 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1167 struct ad_xattr_entry *e = &ad->adx_entries[i];
1168 char *mapped_name = NULL;
1169 char *tmp = NULL;
1170 struct smb_filename *stream_name = NULL;
1171 files_struct *fsp = NULL;
1172 ssize_t nwritten;
1174 status = string_replace_allocate(handle->conn,
1175 e->adx_name,
1176 string_replace_cmaps,
1177 talloc_tos(),
1178 &mapped_name,
1179 vfs_translate_to_windows);
1180 if (!NT_STATUS_IS_OK(status) &&
1181 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1183 DBG_ERR("string_replace_allocate failed\n");
1184 ok = false;
1185 goto fail;
1188 tmp = mapped_name;
1189 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1190 TALLOC_FREE(tmp);
1191 if (mapped_name == NULL) {
1192 ok = false;
1193 goto fail;
1196 stream_name = synthetic_smb_fname(talloc_tos(),
1197 smb_fname->base_name,
1198 mapped_name,
1199 NULL,
1200 smb_fname->twrp,
1201 smb_fname->flags);
1202 TALLOC_FREE(mapped_name);
1203 if (stream_name == NULL) {
1204 DBG_ERR("synthetic_smb_fname failed\n");
1205 ok = false;
1206 goto fail;
1209 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1211 status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1212 if (!NT_STATUS_IS_OK(status) &&
1213 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1215 ok = false;
1216 goto fail;
1219 status = SMB_VFS_CREATE_FILE(
1220 handle->conn, /* conn */
1221 NULL, /* req */
1222 NULL, /* dirfsp */
1223 stream_name, /* fname */
1224 FILE_GENERIC_WRITE, /* access_mask */
1225 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1226 FILE_OPEN_IF, /* create_disposition */
1227 0, /* create_options */
1228 0, /* file_attributes */
1229 INTERNAL_OPEN_ONLY, /* oplock_request */
1230 NULL, /* lease */
1231 0, /* allocation_size */
1232 0, /* private_flags */
1233 NULL, /* sd */
1234 NULL, /* ea_list */
1235 &fsp, /* result */
1236 NULL, /* psbuf */
1237 NULL, NULL); /* create context */
1238 TALLOC_FREE(stream_name);
1239 if (!NT_STATUS_IS_OK(status)) {
1240 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1241 ok = false;
1242 goto fail;
1245 nwritten = SMB_VFS_PWRITE(fsp,
1246 ad->ad_data + e->adx_offset,
1247 e->adx_length,
1249 if (nwritten == -1) {
1250 DBG_ERR("SMB_VFS_PWRITE failed\n");
1251 saved_errno = errno;
1252 close_file_free(NULL, &fsp, ERROR_CLOSE);
1253 errno = saved_errno;
1254 ok = false;
1255 goto fail;
1258 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1259 if (!NT_STATUS_IS_OK(status)) {
1260 ok = false;
1261 goto fail;
1263 fsp = NULL;
1266 ad->adx_header.adx_num_attrs = 0;
1267 TALLOC_FREE(ad->adx_entries);
1269 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1271 rc = ad_fset(handle, ad, ad->ad_fsp);
1272 if (rc != 0) {
1273 DBG_ERR("ad_fset on [%s] failed: %s\n",
1274 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1275 ok = false;
1276 goto fail;
1279 ok = ad_convert_move_reso(handle, ad, smb_fname);
1280 if (!ok) {
1281 goto fail;
1284 *converted_xattr = true;
1285 ok = true;
1287 fail:
1288 return ok;
1291 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1292 struct adouble *ad,
1293 const struct smb_filename *smb_fname)
1295 char *p_ad = NULL;
1296 AfpInfo *ai = NULL;
1297 DATA_BLOB aiblob;
1298 struct smb_filename *stream_name = NULL;
1299 files_struct *fsp = NULL;
1300 size_t size;
1301 ssize_t nwritten;
1302 NTSTATUS status;
1303 int saved_errno = 0;
1304 int cmp;
1306 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1307 if (cmp != 0) {
1308 return true;
1311 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1312 if (p_ad == NULL) {
1313 return false;
1316 ai = afpinfo_new(talloc_tos());
1317 if (ai == NULL) {
1318 return false;
1321 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1323 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1324 if (aiblob.data == NULL) {
1325 TALLOC_FREE(ai);
1326 return false;
1329 size = afpinfo_pack(ai, (char *)aiblob.data);
1330 TALLOC_FREE(ai);
1331 if (size != AFP_INFO_SIZE) {
1332 return false;
1335 stream_name = synthetic_smb_fname(talloc_tos(),
1336 smb_fname->base_name,
1337 AFPINFO_STREAM,
1338 NULL,
1339 smb_fname->twrp,
1340 smb_fname->flags);
1341 if (stream_name == NULL) {
1342 data_blob_free(&aiblob);
1343 DBG_ERR("synthetic_smb_fname failed\n");
1344 return false;
1347 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1349 status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1350 if (!NT_STATUS_IS_OK(status) &&
1351 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1353 return false;
1356 status = SMB_VFS_CREATE_FILE(
1357 handle->conn, /* conn */
1358 NULL, /* req */
1359 NULL, /* dirfsp */
1360 stream_name, /* fname */
1361 FILE_GENERIC_WRITE, /* access_mask */
1362 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1363 FILE_OPEN_IF, /* create_disposition */
1364 0, /* create_options */
1365 0, /* file_attributes */
1366 INTERNAL_OPEN_ONLY, /* oplock_request */
1367 NULL, /* lease */
1368 0, /* allocation_size */
1369 0, /* private_flags */
1370 NULL, /* sd */
1371 NULL, /* ea_list */
1372 &fsp, /* result */
1373 NULL, /* psbuf */
1374 NULL, NULL); /* create context */
1375 TALLOC_FREE(stream_name);
1376 if (!NT_STATUS_IS_OK(status)) {
1377 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1378 return false;
1381 nwritten = SMB_VFS_PWRITE(fsp,
1382 aiblob.data,
1383 aiblob.length,
1385 if (nwritten == -1) {
1386 DBG_ERR("SMB_VFS_PWRITE failed\n");
1387 saved_errno = errno;
1388 close_file_free(NULL, &fsp, ERROR_CLOSE);
1389 errno = saved_errno;
1390 return false;
1393 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1394 if (!NT_STATUS_IS_OK(status)) {
1395 return false;
1397 fsp = NULL;
1399 return true;
1402 static bool ad_convert_truncate(vfs_handle_struct *handle,
1403 struct adouble *ad,
1404 const struct smb_filename *smb_fname)
1406 int rc;
1407 off_t newlen;
1409 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1411 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1412 if (rc != 0) {
1413 return false;
1416 return true;
1419 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1420 struct adouble *ad,
1421 uint32_t flags,
1422 bool *blank)
1424 size_t rforklen = sizeof(empty_resourcefork);
1425 char buf[rforklen];
1426 ssize_t nread;
1427 int cmp;
1428 int rc;
1430 *blank = false;
1432 if (!(flags & AD_CONV_WIPE_BLANK)) {
1433 return true;
1436 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1437 return true;
1440 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1441 if (nread != rforklen) {
1442 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1443 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1444 return false;
1447 cmp = memcmp(buf, empty_resourcefork, rforklen);
1448 if (cmp != 0) {
1449 return true;
1452 ad_setentrylen(ad, ADEID_RFORK, 0);
1454 rc = ad_fset(handle, ad, ad->ad_fsp);
1455 if (rc != 0) {
1456 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1457 return false;
1460 *blank = true;
1461 return true;
1464 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1465 struct adouble *ad,
1466 const struct smb_filename *smb_fname,
1467 uint32_t flags)
1469 struct smb_filename *parent_fname = NULL;
1470 struct smb_filename *at_fname = NULL;
1471 struct smb_filename *ad_name = NULL;
1472 NTSTATUS status;
1473 int rc;
1475 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1476 return true;
1479 if (!(flags & AD_CONV_DELETE)) {
1480 return true;
1483 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1484 if (rc != 0) {
1485 return false;
1488 status = parent_pathref(talloc_tos(),
1489 handle->conn->cwd_fsp,
1490 ad_name,
1491 &parent_fname,
1492 &at_fname);
1493 TALLOC_FREE(ad_name);
1494 if (!NT_STATUS_IS_OK(status)) {
1495 return false;
1498 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1499 parent_fname->fsp,
1500 at_fname,
1502 if (rc != 0) {
1503 DBG_ERR("Unlinking [%s/%s] failed: %s\n",
1504 smb_fname_str_dbg(parent_fname),
1505 smb_fname_str_dbg(at_fname), strerror(errno));
1506 TALLOC_FREE(parent_fname);
1507 return false;
1510 DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1511 smb_fname_str_dbg(parent_fname),
1512 smb_fname_str_dbg(at_fname));
1513 TALLOC_FREE(parent_fname);
1515 return true;
1519 * Convert from Apple's ._ file to Netatalk
1521 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1522 * bytes containing packed xattrs.
1524 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1525 * otherwise
1527 int ad_convert(struct vfs_handle_struct *handle,
1528 const struct smb_filename *smb_fname,
1529 const char *catia_mappings,
1530 uint32_t flags)
1532 struct adouble *ad = NULL;
1533 bool ok;
1534 bool converted_xattr = false;
1535 bool blank;
1536 int ret;
1538 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1539 if (ad == NULL) {
1540 return 0;
1543 ok = ad_convert_xattr(handle,
1545 smb_fname,
1546 catia_mappings,
1547 &converted_xattr);
1548 if (!ok) {
1549 ret = -1;
1550 goto done;
1553 ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1554 if (!ok) {
1555 ret = -1;
1556 goto done;
1559 if (converted_xattr || blank) {
1560 ok = ad_convert_truncate(handle, ad, smb_fname);
1561 if (!ok) {
1562 ret = -1;
1563 goto done;
1567 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1568 if (!ok) {
1569 DBG_ERR("Failed to convert [%s]\n",
1570 smb_fname_str_dbg(smb_fname));
1571 ret = -1;
1572 goto done;
1575 ok = ad_convert_delete_adfile(handle,
1577 smb_fname,
1578 flags);
1579 if (!ok) {
1580 ret = -1;
1581 goto done;
1584 ret = 0;
1585 done:
1586 TALLOC_FREE(ad);
1587 return ret;
1590 static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1591 struct vfs_handle_struct *handle,
1592 struct smb_filename *smb_fname,
1593 struct smb_filename *adpath,
1594 files_struct **_fsp)
1596 files_struct *fsp = NULL;
1597 NTSTATUS status;
1598 int ret;
1600 status = openat_pathref_fsp(handle->conn->cwd_fsp, adpath);
1601 if (!NT_STATUS_IS_OK(status) &&
1602 !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1603 return false;
1606 status = SMB_VFS_CREATE_FILE(
1607 handle->conn,
1608 NULL, /* req */
1609 NULL, /* dirfsp */
1610 adpath,
1611 FILE_READ_DATA|FILE_WRITE_DATA,
1612 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1613 FILE_OPEN_IF,
1614 0, /* create_options */
1615 0, /* file_attributes */
1616 INTERNAL_OPEN_ONLY,
1617 NULL, /* lease */
1618 0, /* allocation_size */
1619 0, /* private_flags */
1620 NULL, /* sd */
1621 NULL, /* ea_list */
1622 &fsp,
1623 NULL, /* info */
1624 NULL, NULL); /* create context */
1625 if (!NT_STATUS_IS_OK(status)) {
1626 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1627 smb_fname_str_dbg(adpath), nt_errstr(status));
1628 return false;
1631 if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1632 fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1634 ret = SMB_VFS_FCHOWN(fsp,
1635 smb_fname->st.st_ex_uid,
1636 smb_fname->st.st_ex_gid);
1637 if (ret != 0) {
1638 DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1639 fsp_str_dbg(fsp), nt_errstr(status));
1640 close_file_free(NULL, &fsp, NORMAL_CLOSE);
1641 return false;
1645 *_fsp = fsp;
1646 return true;
1649 static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1650 struct smb_filename *smb_fname,
1651 TALLOC_CTX *mem_ctx,
1652 unsigned int *num_streams,
1653 struct stream_struct **streams)
1655 files_struct *fsp = NULL;
1656 NTSTATUS status;
1658 status = openat_pathref_fsp(handle->conn->cwd_fsp, smb_fname);
1659 if (!NT_STATUS_IS_OK(status)) {
1660 return false;
1663 status = SMB_VFS_CREATE_FILE(
1664 handle->conn, /* conn */
1665 NULL, /* req */
1666 NULL, /* dirfsp */
1667 smb_fname, /* fname */
1668 FILE_READ_ATTRIBUTES, /* access_mask */
1669 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1670 FILE_SHARE_DELETE),
1671 FILE_OPEN, /* create_disposition*/
1672 0, /* create_options */
1673 0, /* file_attributes */
1674 INTERNAL_OPEN_ONLY, /* oplock_request */
1675 NULL, /* lease */
1676 0, /* allocation_size */
1677 0, /* private_flags */
1678 NULL, /* sd */
1679 NULL, /* ea_list */
1680 &fsp, /* result */
1681 NULL, /* pinfo */
1682 NULL, NULL); /* create context */
1683 if (!NT_STATUS_IS_OK(status)) {
1684 DBG_ERR("Opening [%s] failed: %s\n",
1685 smb_fname_str_dbg(smb_fname),
1686 nt_errstr(status));
1687 return false;
1690 status = vfs_fstreaminfo(fsp,
1691 mem_ctx,
1692 num_streams,
1693 streams);
1694 if (!NT_STATUS_IS_OK(status)) {
1695 close_file_free(NULL, &fsp, NORMAL_CLOSE);
1696 DBG_ERR("streaminfo on [%s] failed: %s\n",
1697 smb_fname_str_dbg(smb_fname),
1698 nt_errstr(status));
1699 return false;
1702 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1703 if (!NT_STATUS_IS_OK(status)) {
1704 DBG_ERR("close_file [%s] failed: %s\n",
1705 smb_fname_str_dbg(smb_fname),
1706 nt_errstr(status));
1707 return false;
1710 return true;
1713 struct ad_collect_state {
1714 bool have_adfile;
1715 size_t adx_data_off;
1716 char *rsrc_data_buf;
1719 static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1720 struct char_mappings **cmaps,
1721 struct smb_filename *smb_fname,
1722 const struct stream_struct *stream,
1723 struct adouble *ad,
1724 struct ad_collect_state *state)
1726 struct smb_filename *sname = NULL;
1727 files_struct *fsp = NULL;
1728 struct ad_xattr_entry *e = NULL;
1729 char *mapped_name = NULL;
1730 char *p = NULL;
1731 size_t needed_size;
1732 ssize_t nread;
1733 NTSTATUS status;
1734 bool ok;
1736 sname = synthetic_smb_fname(ad,
1737 smb_fname->base_name,
1738 stream->name,
1739 NULL,
1740 smb_fname->twrp,
1742 if (sname == NULL) {
1743 return false;
1746 if (is_ntfs_default_stream_smb_fname(sname)) {
1747 TALLOC_FREE(sname);
1748 return true;
1751 DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1753 status = openat_pathref_fsp(handle->conn->cwd_fsp, sname);
1754 if (!NT_STATUS_IS_OK(status)) {
1755 ok = false;
1756 goto out;
1759 status = SMB_VFS_CREATE_FILE(
1760 handle->conn,
1761 NULL, /* req */
1762 NULL, /* dirfsp */
1763 sname,
1764 FILE_READ_DATA|DELETE_ACCESS,
1765 FILE_SHARE_READ,
1766 FILE_OPEN,
1767 0, /* create_options */
1768 0, /* file_attributes */
1769 INTERNAL_OPEN_ONLY, /* oplock_request */
1770 NULL, /* lease */
1771 0, /* allocation_size */
1772 0, /* private_flags */
1773 NULL, /* sd */
1774 NULL, /* ea_list */
1775 &fsp,
1776 NULL, /* info */
1777 NULL, NULL); /* create context */
1778 if (!NT_STATUS_IS_OK(status)) {
1779 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1780 smb_fname_str_dbg(sname));
1781 ok = false;
1782 goto out;
1785 if (is_afpinfo_stream(stream->name)) {
1786 char buf[AFP_INFO_SIZE];
1788 if (stream->size != AFP_INFO_SIZE) {
1789 DBG_ERR("Bad size [%zd] on [%s]\n",
1790 (ssize_t)stream->size,
1791 smb_fname_str_dbg(sname));
1792 ok = false;
1793 goto out;
1796 nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1797 if (nread != AFP_INFO_SIZE) {
1798 DBG_ERR("Bad size [%zd] on [%s]\n",
1799 (ssize_t)stream->size,
1800 smb_fname_str_dbg(sname));
1801 ok = false;
1802 goto out;
1805 memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1806 buf + AFP_OFF_FinderInfo,
1807 AFP_FinderSize);
1809 ok = set_delete_on_close(fsp,
1810 true,
1811 fsp->conn->session_info->security_token,
1812 fsp->conn->session_info->unix_token);
1813 if (!ok) {
1814 DBG_ERR("Deleting [%s] failed\n",
1815 smb_fname_str_dbg(sname));
1816 ok = false;
1817 goto out;
1819 ok = true;
1820 goto out;
1823 if (is_afpresource_stream(stream->name)) {
1824 ad->ad_rsrc_data = talloc_size(ad, stream->size);
1825 if (ad->ad_rsrc_data == NULL) {
1826 ok = false;
1827 goto out;
1830 nread = SMB_VFS_PREAD(fsp,
1831 ad->ad_rsrc_data,
1832 stream->size,
1834 if (nread != stream->size) {
1835 DBG_ERR("Bad size [%zd] on [%s]\n",
1836 (ssize_t)stream->size,
1837 smb_fname_str_dbg(sname));
1838 ok = false;
1839 goto out;
1842 ad_setentrylen(ad, ADEID_RFORK, stream->size);
1844 if (!state->have_adfile) {
1846 * We have a resource *stream* but no AppleDouble
1847 * sidecar file, this means the share is configured with
1848 * fruit:resource=stream. So we should delete the
1849 * resource stream.
1851 ok = set_delete_on_close(
1852 fsp,
1853 true,
1854 fsp->conn->session_info->security_token,
1855 fsp->conn->session_info->unix_token);
1856 if (!ok) {
1857 DBG_ERR("Deleting [%s] failed\n",
1858 smb_fname_str_dbg(sname));
1859 ok = false;
1860 goto out;
1863 ok = true;
1864 goto out;
1867 ad->adx_entries = talloc_realloc(ad,
1868 ad->adx_entries,
1869 struct ad_xattr_entry,
1870 ad->adx_header.adx_num_attrs + 1);
1871 if (ad->adx_entries == NULL) {
1872 ok = false;
1873 goto out;
1876 e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1877 *e = (struct ad_xattr_entry) {
1878 .adx_length = stream->size,
1880 e->adx_name = talloc_strdup(ad, stream->name + 1);
1881 if (e->adx_name == NULL) {
1882 ok = false;
1883 goto out;
1885 p = strchr(e->adx_name, ':');
1886 if (p != NULL) {
1887 *p = '\0';
1890 status = string_replace_allocate(handle->conn,
1891 e->adx_name,
1892 cmaps,
1894 &mapped_name,
1895 vfs_translate_to_unix);
1896 if (!NT_STATUS_IS_OK(status) &&
1897 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1899 DBG_ERR("string_replace_allocate failed\n");
1900 ok = false;
1901 goto out;
1904 e->adx_name = mapped_name;
1905 e->adx_namelen = strlen(e->adx_name) + 1,
1907 DBG_DEBUG("%u: name (%s) size (%zu)\n",
1908 ad->adx_header.adx_num_attrs,
1909 e->adx_name,
1910 (size_t)e->adx_length);
1912 ad->adx_header.adx_num_attrs++;
1914 needed_size = state->adx_data_off + stream->size;
1915 if (needed_size > talloc_get_size(ad->adx_data)) {
1916 ad->adx_data = talloc_realloc(ad,
1917 ad->adx_data,
1918 char,
1919 needed_size);
1920 if (ad->adx_data == NULL) {
1921 ok = false;
1922 goto out;
1926 nread = SMB_VFS_PREAD(fsp,
1927 ad->adx_data + state->adx_data_off,
1928 stream->size,
1930 if (nread != stream->size) {
1931 DBG_ERR("Bad size [%zd] on [%s]\n",
1932 (ssize_t)stream->size,
1933 smb_fname_str_dbg(sname));
1934 ok = false;
1935 goto out;
1937 state->adx_data_off += nread;
1939 ok = set_delete_on_close(fsp,
1940 true,
1941 fsp->conn->session_info->security_token,
1942 fsp->conn->session_info->unix_token);
1943 if (!ok) {
1944 DBG_ERR("Deleting [%s] failed\n",
1945 smb_fname_str_dbg(sname));
1946 ok = false;
1947 goto out;
1950 out:
1951 TALLOC_FREE(sname);
1952 if (fsp != NULL) {
1953 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1954 if (!NT_STATUS_IS_OK(status)) {
1955 DBG_ERR("close_file [%s] failed: %s\n",
1956 smb_fname_str_dbg(smb_fname),
1957 nt_errstr(status));
1958 ok = false;
1962 return ok;
1966 * Convert filesystem metadata to AppleDouble file
1968 bool ad_unconvert(TALLOC_CTX *mem_ctx,
1969 struct vfs_handle_struct *handle,
1970 const char *catia_mappings,
1971 struct smb_filename *smb_fname,
1972 bool *converted)
1974 static struct char_mappings **cmaps = NULL;
1975 TALLOC_CTX *frame = talloc_stackframe();
1976 struct ad_collect_state state;
1977 struct stream_struct *streams = NULL;
1978 struct smb_filename *adpath = NULL;
1979 struct adouble *ad = NULL;
1980 unsigned int num_streams = 0;
1981 size_t to_convert = 0;
1982 bool have_rsrc = false;
1983 files_struct *fsp = NULL;
1984 size_t i;
1985 NTSTATUS status;
1986 int ret;
1987 bool ok;
1989 *converted = false;
1991 if (cmaps == NULL) {
1992 const char **mappings = NULL;
1994 mappings = str_list_make_v3_const(
1995 frame, catia_mappings, NULL);
1996 if (mappings == NULL) {
1997 ok = false;
1998 goto out;
2000 cmaps = string_replace_init_map(mem_ctx, mappings);
2001 TALLOC_FREE(mappings);
2004 ok = ad_unconvert_get_streams(handle,
2005 smb_fname,
2006 frame,
2007 &num_streams,
2008 &streams);
2009 if (!ok) {
2010 goto out;
2013 for (i = 0; i < num_streams; i++) {
2014 if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
2015 continue;
2017 to_convert++;
2018 if (is_afpresource_stream(streams[i].name)) {
2019 have_rsrc = true;
2023 if (to_convert == 0) {
2024 ok = true;
2025 goto out;
2028 state = (struct ad_collect_state) {
2029 .adx_data_off = 0,
2032 ret = adouble_path(frame, smb_fname, &adpath);
2033 if (ret != 0) {
2034 ok = false;
2035 goto out;
2038 ret = SMB_VFS_STAT(handle->conn, adpath);
2039 if (ret == 0) {
2040 state.have_adfile = true;
2041 } else {
2042 if (errno != ENOENT) {
2043 ok = false;
2044 goto out;
2046 state.have_adfile = false;
2049 if (to_convert == 1 && have_rsrc && state.have_adfile) {
2051 * So we have just a single stream, the resource fork stream
2052 * from an AppleDouble file. Fine, that means there's nothing to
2053 * convert.
2055 ok = true;
2056 goto out;
2059 ad = ad_init(frame, ADOUBLE_RSRC);
2060 if (ad == NULL) {
2061 ok = false;
2062 goto out;
2065 for (i = 0; i < num_streams; i++) {
2066 ok = ad_collect_one_stream(handle,
2067 cmaps,
2068 smb_fname,
2069 &streams[i],
2071 &state);
2072 if (!ok) {
2073 goto out;
2077 ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
2078 if (!ok) {
2079 DBG_ERR("Failed to open adfile [%s]\n",
2080 smb_fname_str_dbg(smb_fname));
2081 goto out;
2084 ret = ad_fset(handle, ad, fsp);
2085 if (ret != 0) {
2086 ok = false;
2087 goto out;
2090 *converted = true;
2091 ok = true;
2093 out:
2094 if (fsp != NULL) {
2095 status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
2096 if (!NT_STATUS_IS_OK(status)) {
2097 DBG_ERR("close_file_free() [%s] failed: %s\n",
2098 smb_fname_str_dbg(smb_fname),
2099 nt_errstr(status));
2100 ok = false;
2103 TALLOC_FREE(frame);
2104 return ok;
2108 * Read and parse Netatalk AppleDouble metadata xattr
2110 static ssize_t ad_read_meta(vfs_handle_struct *handle,
2111 struct adouble *ad,
2112 const struct smb_filename *smb_fname)
2114 int rc = 0;
2115 ssize_t ealen;
2116 bool ok;
2117 struct files_struct *fsp = smb_fname->fsp;
2119 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
2121 fsp = metadata_fsp(fsp);
2123 ealen = SMB_VFS_FGETXATTR(fsp,
2124 AFPINFO_EA_NETATALK,
2125 ad->ad_data,
2126 AD_DATASZ_XATTR);
2128 if (ealen == -1) {
2129 switch (errno) {
2130 case ENOATTR:
2131 case ENOENT:
2132 if (errno == ENOATTR) {
2133 errno = ENOENT;
2135 rc = -1;
2136 goto exit;
2137 default:
2138 DEBUG(2, ("error reading meta xattr: %s\n",
2139 strerror(errno)));
2140 rc = -1;
2141 goto exit;
2144 if (ealen != AD_DATASZ_XATTR) {
2145 DEBUG(2, ("bad size %zd\n", ealen));
2146 errno = EINVAL;
2147 rc = -1;
2148 goto exit;
2151 /* Now parse entries */
2152 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2153 if (!ok) {
2154 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2155 errno = EINVAL;
2156 rc = -1;
2157 goto exit;
2160 if (!ad_getentryoff(ad, ADEID_FINDERI)
2161 || !ad_getentryoff(ad, ADEID_COMMENT)
2162 || !ad_getentryoff(ad, ADEID_FILEDATESI)
2163 || !ad_getentryoff(ad, ADEID_AFPFILEI)
2164 || !ad_getentryoff(ad, ADEID_PRIVDEV)
2165 || !ad_getentryoff(ad, ADEID_PRIVINO)
2166 || !ad_getentryoff(ad, ADEID_PRIVSYN)
2167 || !ad_getentryoff(ad, ADEID_PRIVID)) {
2168 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2169 errno = EINVAL;
2170 rc = -1;
2171 goto exit;
2174 exit:
2175 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2176 smb_fname->base_name, rc));
2178 if (rc != 0) {
2179 ealen = -1;
2180 if (errno == EINVAL) {
2181 become_root();
2182 (void)SMB_VFS_FREMOVEXATTR(fsp,
2183 AFPINFO_EA_NETATALK);
2184 unbecome_root();
2185 errno = ENOENT;
2188 return ealen;
2191 static NTSTATUS adouble_open_rsrc_fsp(const struct files_struct *dirfsp,
2192 const struct smb_filename *smb_base_fname,
2193 int in_flags,
2194 mode_t mode,
2195 struct files_struct **_ad_fsp)
2197 int rc = 0;
2198 struct adouble *ad = NULL;
2199 struct smb_filename *adp_smb_fname = NULL;
2200 struct files_struct *ad_fsp = NULL;
2201 NTSTATUS status;
2202 struct vfs_open_how how = { .flags = in_flags, .mode = mode, };
2204 rc = adouble_path(talloc_tos(),
2205 smb_base_fname,
2206 &adp_smb_fname);
2207 if (rc != 0) {
2208 return NT_STATUS_NO_MEMORY;
2211 status = create_internal_fsp(dirfsp->conn,
2212 adp_smb_fname,
2213 &ad_fsp);
2214 if (!NT_STATUS_IS_OK(status)) {
2215 return status;
2218 #ifdef O_PATH
2219 how.flags &= ~(O_PATH);
2220 #endif
2221 if (how.flags & (O_CREAT | O_TRUNC | O_WRONLY)) {
2222 /* We always need read/write access for the metadata header too */
2223 how.flags &= ~(O_WRONLY);
2224 how.flags |= O_RDWR;
2227 status = fd_openat(dirfsp,
2228 adp_smb_fname,
2229 ad_fsp,
2230 &how);
2231 if (!NT_STATUS_IS_OK(status)) {
2232 file_free(NULL, ad_fsp);
2233 return status;
2236 if (how.flags & (O_CREAT | O_TRUNC)) {
2237 ad = ad_init(talloc_tos(), ADOUBLE_RSRC);
2238 if (ad == NULL) {
2239 file_free(NULL, ad_fsp);
2240 return NT_STATUS_NO_MEMORY;
2243 rc = ad_fset(ad_fsp->conn->vfs_handles, ad, ad_fsp);
2244 if (rc != 0) {
2245 file_free(NULL, ad_fsp);
2246 return NT_STATUS_IO_DEVICE_ERROR;
2248 TALLOC_FREE(ad);
2251 *_ad_fsp = ad_fsp;
2252 return NT_STATUS_OK;
2255 NTSTATUS adouble_open_from_base_fsp(const struct files_struct *dirfsp,
2256 struct files_struct *base_fsp,
2257 adouble_type_t type,
2258 int flags,
2259 mode_t mode,
2260 struct files_struct **_ad_fsp)
2262 *_ad_fsp = NULL;
2264 SMB_ASSERT(base_fsp != NULL);
2265 SMB_ASSERT(!fsp_is_alternate_stream(base_fsp));
2267 switch (type) {
2268 case ADOUBLE_META:
2269 return NT_STATUS_INTERNAL_ERROR;
2270 case ADOUBLE_RSRC:
2271 return adouble_open_rsrc_fsp(dirfsp,
2272 base_fsp->fsp_name,
2273 flags,
2274 mode,
2275 _ad_fsp);
2278 return NT_STATUS_INTERNAL_ERROR;
2282 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2283 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2284 * for file IO on the ._ file.
2286 static int ad_open(vfs_handle_struct *handle,
2287 struct adouble *ad,
2288 files_struct *fsp,
2289 const struct smb_filename *smb_fname,
2290 int flags,
2291 mode_t mode)
2293 NTSTATUS status;
2295 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2296 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2298 if (ad->ad_type == ADOUBLE_META) {
2299 return 0;
2302 if (fsp != NULL) {
2303 ad->ad_fsp = fsp;
2304 ad->ad_opened = false;
2305 return 0;
2308 status = adouble_open_rsrc_fsp(handle->conn->cwd_fsp,
2309 smb_fname,
2310 flags,
2311 mode,
2312 &ad->ad_fsp);
2313 if (!NT_STATUS_IS_OK(status)) {
2314 errno = map_errno_from_nt_status(status);
2315 return -1;
2317 ad->ad_opened = true;
2319 DBG_DEBUG("Path [%s] type [%s]\n",
2320 smb_fname->base_name,
2321 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2323 return 0;
2326 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2327 struct adouble *ad,
2328 const struct smb_filename *smb_fname)
2330 size_t to_read;
2331 ssize_t len;
2332 int ret;
2333 bool ok;
2335 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2336 if (ret != 0) {
2337 DBG_ERR("fstat [%s] failed: %s\n",
2338 fsp_str_dbg(ad->ad_fsp), strerror(errno));
2339 return -1;
2342 to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2343 if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2344 to_read = AD_XATTR_MAX_HDR_SIZE;
2347 len = SMB_VFS_NEXT_PREAD(handle,
2348 ad->ad_fsp,
2349 ad->ad_data,
2350 to_read,
2352 if (len != to_read) {
2353 DBG_NOTICE("%s %s: bad size: %zd\n",
2354 smb_fname->base_name, strerror(errno), len);
2355 return -1;
2358 /* Now parse entries */
2359 ok = ad_unpack(ad,
2360 ADEID_NUM_DOT_UND,
2361 ad->ad_fsp->fsp_name->st.st_ex_size);
2362 if (!ok) {
2363 DBG_ERR("invalid AppleDouble resource %s\n",
2364 smb_fname->base_name);
2365 errno = EINVAL;
2366 return -1;
2369 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2370 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2371 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2373 DBG_ERR("invalid AppleDouble resource %s\n",
2374 smb_fname->base_name);
2375 errno = EINVAL;
2376 return -1;
2379 return len;
2383 * Read and parse resource fork, either ._ AppleDouble file or xattr
2385 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2386 struct adouble *ad,
2387 const struct smb_filename *smb_fname)
2389 return ad_read_rsrc_adouble(handle, ad, smb_fname);
2393 * Read and unpack an AppleDouble metadata xattr or resource
2395 static ssize_t ad_read(vfs_handle_struct *handle,
2396 struct adouble *ad,
2397 const struct smb_filename *smb_fname)
2399 switch (ad->ad_type) {
2400 case ADOUBLE_META:
2401 return ad_read_meta(handle, ad, smb_fname);
2402 case ADOUBLE_RSRC:
2403 return ad_read_rsrc(handle, ad, smb_fname);
2404 default:
2405 return -1;
2409 static int adouble_destructor(struct adouble *ad)
2411 NTSTATUS status;
2413 if (!ad->ad_opened) {
2414 return 0;
2417 SMB_ASSERT(ad->ad_fsp != NULL);
2419 status = fd_close(ad->ad_fsp);
2420 if (!NT_STATUS_IS_OK(status)) {
2421 DBG_ERR("Closing [%s] failed: %s\n",
2422 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2424 file_free(NULL, ad->ad_fsp);
2425 ad->ad_fsp = NULL;
2426 ad->ad_opened = false;
2428 return 0;
2432 * Allocate a struct adouble without initialiing it
2434 * The struct is either hang of the fsp extension context or if fsp is
2435 * NULL from ctx.
2437 * @param[in] ctx talloc context
2438 * @param[in] handle vfs handle
2439 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2441 * @return adouble handle
2443 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2444 adouble_type_t type)
2446 int rc = 0;
2447 size_t adsize = 0;
2448 struct adouble *ad;
2450 switch (type) {
2451 case ADOUBLE_META:
2452 adsize = AD_DATASZ_XATTR;
2453 break;
2454 case ADOUBLE_RSRC:
2456 * AppleDouble ._ file case, optimize for fewer (but larger)
2457 * IOs. Two cases:
2459 * - without xattrs size of the header is exactly
2460 * AD_DATASZ_DOT_UND (82) bytes
2462 * - with embedded xattrs it can be larger, up to
2463 * AD_XATTR_MAX_HDR_SIZE
2465 * Larger headers are not supported, but this is a reasonable
2466 * limit that is also employed by the macOS client.
2468 * We used the largest possible size to be able to read the full
2469 * header with one IO.
2471 adsize = AD_XATTR_MAX_HDR_SIZE;
2472 break;
2473 default:
2474 return NULL;
2477 ad = talloc_zero(ctx, struct adouble);
2478 if (ad == NULL) {
2479 rc = -1;
2480 goto exit;
2483 if (adsize) {
2484 ad->ad_data = talloc_zero_array(ad, char, adsize);
2485 if (ad->ad_data == NULL) {
2486 rc = -1;
2487 goto exit;
2491 ad->ad_type = type;
2492 ad->ad_magic = AD_MAGIC;
2493 ad->ad_version = AD_VERSION;
2495 talloc_set_destructor(ad, adouble_destructor);
2497 exit:
2498 if (rc != 0) {
2499 TALLOC_FREE(ad);
2501 return ad;
2505 * Allocate and initialize a new struct adouble
2507 * @param[in] ctx talloc context
2508 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2510 * @return adouble handle, initialized
2512 struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2514 int rc = 0;
2515 const struct ad_entry_order *eid;
2516 struct adouble *ad = NULL;
2517 time_t t = time(NULL);
2519 switch (type) {
2520 case ADOUBLE_META:
2521 eid = entry_order_meta_xattr;
2522 break;
2523 case ADOUBLE_RSRC:
2524 eid = entry_order_dot_und;
2525 break;
2526 default:
2527 return NULL;
2530 ad = ad_alloc(ctx, type);
2531 if (ad == NULL) {
2532 return NULL;
2535 while (eid->id) {
2536 ad->ad_eid[eid->id].ade_off = eid->offset;
2537 ad->ad_eid[eid->id].ade_len = eid->len;
2538 eid++;
2541 /* put something sane in the date fields */
2542 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2543 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2544 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2545 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2547 if (rc != 0) {
2548 TALLOC_FREE(ad);
2550 return ad;
2553 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2554 vfs_handle_struct *handle,
2555 files_struct *fsp,
2556 const struct smb_filename *smb_fname,
2557 adouble_type_t type)
2559 int rc = 0;
2560 ssize_t len;
2561 struct adouble *ad = NULL;
2562 int mode;
2564 if (fsp != NULL) {
2565 if (fsp_is_alternate_stream(fsp)) {
2566 smb_fname = fsp->base_fsp->fsp_name;
2567 } else {
2568 smb_fname = fsp->fsp_name;
2572 DEBUG(10, ("ad_get(%s) called for %s\n",
2573 type == ADOUBLE_META ? "meta" : "rsrc",
2574 smb_fname != NULL ? smb_fname->base_name : "???"));
2576 ad = ad_alloc(ctx, type);
2577 if (ad == NULL) {
2578 rc = -1;
2579 goto exit;
2582 /* Try rw first so we can use the fd in ad_convert() */
2583 mode = O_RDWR;
2585 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2586 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2587 mode = O_RDONLY;
2588 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2590 if (rc == -1) {
2591 DBG_DEBUG("ad_open [%s] error [%s]\n",
2592 smb_fname->base_name, strerror(errno));
2593 goto exit;
2597 len = ad_read(handle, ad, smb_fname);
2598 if (len == -1) {
2599 DEBUG(10, ("error reading AppleDouble for %s\n",
2600 smb_fname->base_name));
2601 rc = -1;
2602 goto exit;
2605 exit:
2606 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2607 type == ADOUBLE_META ? "meta" : "rsrc",
2608 smb_fname->base_name, rc));
2610 if (rc != 0) {
2611 TALLOC_FREE(ad);
2613 return ad;
2617 * Return AppleDouble data for a file
2619 * @param[in] ctx talloc context
2620 * @param[in] handle vfs handle
2621 * @param[in] smb_fname pathname to file or directory
2622 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2624 * @return talloced struct adouble or NULL on error
2626 struct adouble *ad_get(TALLOC_CTX *ctx,
2627 vfs_handle_struct *handle,
2628 const struct smb_filename *smb_fname,
2629 adouble_type_t type)
2631 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2635 * Return AppleDouble data for a file
2637 * @param[in] ctx talloc context
2638 * @param[in] handle vfs handle
2639 * @param[in] fsp fsp to use for IO
2640 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2642 * @return talloced struct adouble or NULL on error
2644 struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2645 files_struct *fsp, adouble_type_t type)
2647 return ad_get_internal(ctx, handle, fsp, NULL, type);
2651 * Set AppleDouble metadata on a file or directory
2653 * @param[in] ad adouble handle
2654 * @param[in] fsp file handle
2656 * @return status code, 0 means success
2658 int ad_fset(struct vfs_handle_struct *handle,
2659 struct adouble *ad,
2660 files_struct *fsp)
2662 int rc = -1;
2663 ssize_t len;
2664 bool ok;
2666 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2668 ok = ad_pack(handle, ad, fsp);
2669 if (!ok) {
2670 return -1;
2673 switch (ad->ad_type) {
2674 case ADOUBLE_META:
2675 rc = SMB_VFS_NEXT_FSETXATTR(handle,
2676 fsp->base_fsp ? fsp->base_fsp : fsp,
2677 AFPINFO_EA_NETATALK,
2678 ad->ad_data,
2679 AD_DATASZ_XATTR, 0);
2680 break;
2681 case ADOUBLE_RSRC:
2682 len = SMB_VFS_NEXT_PWRITE(handle,
2683 fsp,
2684 ad->ad_data,
2685 ad_getentryoff(ad, ADEID_RFORK),
2687 if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2688 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2689 return -1;
2691 rc = 0;
2692 break;
2694 default:
2695 return -1;
2698 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2700 return rc;
2703 bool is_adouble_file(const char *path)
2705 const char *p = NULL;
2706 int match;
2708 p = strrchr(path, '/');
2709 if (p == NULL) {
2710 p = path;
2711 } else {
2712 p++;
2715 match = strncmp(p,
2716 ADOUBLE_NAME_PREFIX,
2717 strlen(ADOUBLE_NAME_PREFIX));
2718 if (match != 0) {
2719 return false;
2721 return true;
2725 * Prepend "._" to a basename
2726 * Return a new struct smb_filename with stream_name == NULL.
2728 int adouble_path(TALLOC_CTX *ctx,
2729 const struct smb_filename *smb_fname_in,
2730 struct smb_filename **pp_smb_fname_out)
2732 char *parent;
2733 const char *base;
2734 struct smb_filename *smb_fname = NULL;
2736 smb_fname = cp_smb_filename_nostream(ctx, smb_fname_in);
2737 if (smb_fname == NULL) {
2738 return -1;
2741 /* We're replacing base_name. */
2742 TALLOC_FREE(smb_fname->base_name);
2744 SET_STAT_INVALID(smb_fname->st);
2746 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2747 &parent, &base)) {
2748 TALLOC_FREE(smb_fname);
2749 return -1;
2752 if (ISDOT(parent)) {
2753 smb_fname->base_name = talloc_asprintf(smb_fname,
2754 "._%s", base);
2755 } else {
2756 smb_fname->base_name = talloc_asprintf(smb_fname,
2757 "%s/._%s", parent, base);
2759 if (smb_fname->base_name == NULL) {
2760 TALLOC_FREE(smb_fname);
2761 return -1;
2764 *pp_smb_fname_out = smb_fname;
2766 return 0;
2770 * Allocate and initialize an AfpInfo struct
2772 AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2774 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2775 if (ai == NULL) {
2776 return NULL;
2778 ai->afpi_Signature = AFP_Signature;
2779 ai->afpi_Version = AFP_Version;
2780 ai->afpi_BackupTime = AD_DATE_START;
2781 return ai;
2785 * Pack an AfpInfo struct into a buffer
2787 * Buffer size must be at least AFP_INFO_SIZE
2788 * Returns size of packed buffer
2790 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2792 memset(buf, 0, AFP_INFO_SIZE);
2794 RSIVAL(buf, 0, ai->afpi_Signature);
2795 RSIVAL(buf, 4, ai->afpi_Version);
2796 RSIVAL(buf, 12, ai->afpi_BackupTime);
2797 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2799 return AFP_INFO_SIZE;
2803 * Unpack a buffer into a AfpInfo structure
2805 * Buffer size must be at least AFP_INFO_SIZE
2806 * Returns allocated AfpInfo struct
2808 AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2810 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2811 if (ai == NULL) {
2812 return NULL;
2815 ai->afpi_Signature = RIVAL(data, 0);
2816 ai->afpi_Version = RIVAL(data, 4);
2817 ai->afpi_BackupTime = RIVAL(data, 12);
2818 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2819 sizeof(ai->afpi_FinderInfo));
2821 if (ai->afpi_Signature != AFP_Signature
2822 || ai->afpi_Version != AFP_Version) {
2823 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2824 TALLOC_FREE(ai);
2827 return ai;