Line data Source code
1 : /*
2 : * Samba AppleDouble helpers
3 : *
4 : * Copyright (C) Ralph Boehme, 2019
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "adouble.h"
22 : #include "MacExtensions.h"
23 : #include "string_replace.h"
24 : #include "smbd/smbd.h"
25 : #include "system/filesys.h"
26 : #include "libcli/security/security.h"
27 : #include "lib/util_macstreams.h"
28 : #include "auth.h"
29 :
30 : /*
31 : "._" AppleDouble Header File Layout:
32 :
33 : MAGIC 0x00051607
34 : VERSION 0x00020000
35 : FILLER 0
36 : COUNT 2
37 : .-- AD ENTRY[0] Finder Info Entry (must be first)
38 : .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
39 : | | /////////////
40 : | '-> FINDER INFO Fixed Size Data (32 Bytes)
41 : | ~~~~~~~~~~~~~ 2 Bytes Padding
42 : | EXT ATTR HDR Fixed Size Data (36 Bytes)
43 : | /////////////
44 : | ATTR ENTRY[0] --.
45 : | ATTR ENTRY[1] --+--.
46 : | ATTR ENTRY[2] --+--+--.
47 : | ... | | |
48 : | ATTR ENTRY[N] --+--+--+--.
49 : | ATTR DATA 0 <-' | | |
50 : | //////////// | | |
51 : | ATTR DATA 1 <----' | |
52 : | ///////////// | |
53 : | ATTR DATA 2 <-------' |
54 : | ///////////// |
55 : | ... |
56 : | ATTR DATA N <----------'
57 : | /////////////
58 : | ... Attribute Free Space
59 : |
60 : '----> RESOURCE FORK
61 : ... Variable Sized Data
62 : ...
63 : */
64 :
65 : /* Number of actually used entries */
66 : #define ADEID_NUM_XATTR 8
67 : #define ADEID_NUM_DOT_UND 2
68 : #define ADEID_NUM_RSRC_XATTR 1
69 :
70 : /* Sizes of relevant entry bits */
71 : #define ADEDLEN_MAGIC 4
72 : #define ADEDLEN_VERSION 4
73 : #define ADEDLEN_FILLER 16
74 : #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
75 : #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
76 : #define ADEDLEN_NENTRIES 2
77 : #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
78 : ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
79 : #define AD_ENTRY_LEN_EID 4
80 : #define AD_ENTRY_LEN_OFF 4
81 : #define AD_ENTRY_LEN_LEN 4
82 : #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
83 :
84 : /* Offsets */
85 : #define ADEDOFF_MAGIC 0
86 : #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
87 : #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
88 : #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
89 :
90 : #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
91 : (ADEID_NUM_XATTR * AD_ENTRY_LEN))
92 : #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
93 : #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
94 : #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
95 : ADEDLEN_FILEDATESI)
96 : #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
97 : #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
98 : #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
99 : #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
100 :
101 : #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
102 : (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
103 : #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
104 :
105 : #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
106 : (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
107 : ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
108 : ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
109 : ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
110 : ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
111 :
112 : #if AD_DATASZ_XATTR != 402
113 : #error bad size for AD_DATASZ_XATTR
114 : #endif
115 :
116 : #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
117 : (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
118 : ADEDLEN_FINDERI)
119 : #if AD_DATASZ_DOT_UND != 82
120 : #error bad size for AD_DATASZ_DOT_UND
121 : #endif
122 :
123 : #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
124 : #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
125 : #define AD_XATTR_HDR_SIZE 36
126 : #define AD_XATTR_MAX_HDR_SIZE 65536
127 : #define ADX_ENTRY_FIXED_SIZE (4+4+2+1)
128 :
129 : /*
130 : * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
131 : * representation as well as the on-disk format.
132 : *
133 : * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
134 : * the length of the FinderInfo entry is larger then 32 bytes. It is then
135 : * preceeded with 2 bytes padding.
136 : *
137 : * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
138 : */
139 :
140 : struct ad_xattr_header {
141 : uint32_t adx_magic; /* ATTR_HDR_MAGIC */
142 : uint32_t adx_debug_tag; /* for debugging == file id of owning file */
143 : uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
144 : uint32_t adx_data_start; /* file offset to attribute data area */
145 : uint32_t adx_data_length; /* length of attribute data area */
146 : uint32_t adx_reserved[3];
147 : uint16_t adx_flags;
148 : uint16_t adx_num_attrs;
149 : };
150 :
151 : /* On-disk entries are aligned on 4 byte boundaries */
152 : struct ad_xattr_entry {
153 : uint32_t adx_offset; /* file offset to data */
154 : uint32_t adx_length; /* size of attribute data */
155 : uint16_t adx_flags;
156 : uint8_t adx_namelen; /* included the NULL terminator */
157 : char *adx_name; /* NULL-terminated UTF-8 name */
158 : };
159 :
160 : struct ad_entry {
161 : size_t ade_off;
162 : size_t ade_len;
163 : };
164 :
165 : struct adouble {
166 : files_struct *ad_fsp;
167 : bool ad_opened;
168 : adouble_type_t ad_type;
169 : uint32_t ad_magic;
170 : uint32_t ad_version;
171 : uint8_t ad_filler[ADEDLEN_FILLER];
172 : struct ad_entry ad_eid[ADEID_MAX];
173 : char *ad_data;
174 : char *ad_rsrc_data;
175 : struct ad_xattr_header adx_header;
176 : struct ad_xattr_entry *adx_entries;
177 : char *adx_data;
178 : };
179 :
180 : struct ad_entry_order {
181 : uint32_t id, offset, len;
182 : };
183 :
184 : /* Netatalk AppleDouble metadata xattr */
185 : static const
186 : struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
187 : {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
188 : {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
189 : {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
190 : {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
191 : {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
192 : {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
193 : {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
194 : {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
195 : {0, 0, 0}
196 : };
197 :
198 : /* AppleDouble resource fork file (the ones prefixed by "._") */
199 : static const
200 : struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
201 : {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
202 : {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
203 : {0, 0, 0}
204 : };
205 :
206 : /* Conversion from enumerated id to on-disk AppleDouble id */
207 : #define AD_EID_DISK(a) (set_eid[a])
208 : static const uint32_t set_eid[] = {
209 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
210 : AD_DEV, AD_INO, AD_SYN, AD_ID
211 : };
212 :
213 : static char empty_resourcefork[] = {
214 : 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
215 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
216 : 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
217 : 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
218 : 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
219 : 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
220 : 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
221 : 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
222 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 : 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
247 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
248 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 : 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
250 : };
251 :
252 0 : size_t ad_getentrylen(const struct adouble *ad, int eid)
253 : {
254 0 : return ad->ad_eid[eid].ade_len;
255 : }
256 :
257 0 : size_t ad_getentryoff(const struct adouble *ad, int eid)
258 : {
259 0 : return ad->ad_eid[eid].ade_off;
260 : }
261 :
262 0 : size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
263 : {
264 0 : return ad->ad_eid[eid].ade_len = len;
265 : }
266 :
267 0 : size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
268 : {
269 0 : return ad->ad_eid[eid].ade_off = off;
270 : }
271 :
272 : /*
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.
277 : *
278 : * The resource fork is never accessed directly by the ad_data buf.
279 : */
280 0 : static bool ad_entry_check_size(uint32_t eid,
281 : size_t bufsize,
282 : uint32_t off,
283 : uint32_t got_len)
284 : {
285 : struct {
286 : off_t expected_len;
287 : bool fixed_size;
288 : bool minimum_size;
289 0 : } 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},
309 : };
310 :
311 0 : if (eid >= ADEID_MAX) {
312 0 : return false;
313 : }
314 0 : if (got_len == 0) {
315 : /* Entry present, but empty, allow */
316 0 : return true;
317 : }
318 0 : if (ad_checks[eid].expected_len == 0) {
319 : /*
320 : * Shouldn't happen: implicitly initialized to zero because
321 : * explicit initializer missing.
322 : */
323 0 : return false;
324 : }
325 0 : if (ad_checks[eid].expected_len == -1) {
326 : /* Unused or no limit */
327 0 : return true;
328 : }
329 0 : if (ad_checks[eid].fixed_size) {
330 0 : if (ad_checks[eid].expected_len != got_len) {
331 : /* Wrong size fo fixed size entry. */
332 0 : return false;
333 : }
334 : } else {
335 0 : if (ad_checks[eid].minimum_size) {
336 0 : if (got_len < ad_checks[eid].expected_len) {
337 : /*
338 : * Too small for variable sized entry with
339 : * minimum size.
340 : */
341 0 : return false;
342 : }
343 : } else {
344 0 : if (got_len > ad_checks[eid].expected_len) {
345 : /* Too big for variable sized entry. */
346 0 : return false;
347 : }
348 : }
349 : }
350 0 : if (off + got_len < off) {
351 : /* wrap around */
352 0 : return false;
353 : }
354 0 : if (off + got_len > bufsize) {
355 : /* overflow */
356 0 : return false;
357 : }
358 0 : return true;
359 : }
360 :
361 : /**
362 : * Return a pointer to an AppleDouble entry
363 : *
364 : * Returns NULL if the entry is not present
365 : **/
366 0 : char *ad_get_entry(const struct adouble *ad, int eid)
367 : {
368 0 : size_t bufsize = talloc_get_size(ad->ad_data);
369 0 : off_t off = ad_getentryoff(ad, eid);
370 0 : size_t len = ad_getentrylen(ad, eid);
371 : bool valid;
372 :
373 0 : valid = ad_entry_check_size(eid, bufsize, off, len);
374 0 : if (!valid) {
375 0 : return NULL;
376 : }
377 :
378 0 : if (off == 0 || len == 0) {
379 0 : return NULL;
380 : }
381 :
382 0 : return ad->ad_data + off;
383 : }
384 :
385 : /**
386 : * Get a date
387 : **/
388 0 : int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
389 : {
390 0 : bool xlate = (dateoff & AD_DATE_UNIX);
391 0 : char *p = NULL;
392 :
393 0 : dateoff &= AD_DATE_MASK;
394 0 : p = ad_get_entry(ad, ADEID_FILEDATESI);
395 0 : if (p == NULL) {
396 0 : return -1;
397 : }
398 :
399 0 : if (dateoff > AD_DATE_ACCESS) {
400 0 : return -1;
401 : }
402 :
403 0 : memcpy(date, p + dateoff, sizeof(uint32_t));
404 :
405 0 : if (xlate) {
406 0 : *date = AD_DATE_TO_UNIX(*date);
407 : }
408 0 : return 0;
409 : }
410 :
411 : /**
412 : * Set a date
413 : **/
414 0 : int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
415 : {
416 0 : bool xlate = (dateoff & AD_DATE_UNIX);
417 0 : char *p = NULL;
418 :
419 0 : p = ad_get_entry(ad, ADEID_FILEDATESI);
420 0 : if (p == NULL) {
421 0 : return -1;
422 : }
423 :
424 0 : dateoff &= AD_DATE_MASK;
425 0 : if (xlate) {
426 0 : date = AD_DATE_FROM_UNIX(date);
427 : }
428 :
429 0 : if (dateoff > AD_DATE_ACCESS) {
430 0 : return -1;
431 : }
432 :
433 0 : memcpy(p + dateoff, &date, sizeof(date));
434 :
435 0 : return 0;
436 : }
437 :
438 :
439 : /**
440 : * Map on-disk AppleDouble id to enumerated id
441 : **/
442 0 : static uint32_t get_eid(uint32_t eid)
443 : {
444 0 : if (eid <= 15) {
445 0 : return eid;
446 : }
447 :
448 0 : switch (eid) {
449 0 : case AD_DEV:
450 0 : return ADEID_PRIVDEV;
451 0 : case AD_INO:
452 0 : return ADEID_PRIVINO;
453 0 : case AD_SYN:
454 0 : return ADEID_PRIVSYN;
455 0 : case AD_ID:
456 0 : return ADEID_PRIVID;
457 0 : default:
458 0 : break;
459 : }
460 :
461 0 : return 0;
462 : }
463 :
464 : /*
465 : * Move resourcefork data in an AppleDouble file
466 : *
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.
470 : *
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.
474 : */
475 0 : static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
476 : struct adouble *ad,
477 : files_struct *fsp)
478 : {
479 : size_t reso_len;
480 : size_t reso_off;
481 : size_t n;
482 : bool ok;
483 :
484 0 : reso_len = ad_getentrylen(ad, ADEID_RFORK);
485 0 : reso_off = ad_getentryoff(ad, ADEID_RFORK);
486 :
487 0 : if (reso_len == 0) {
488 0 : return true;
489 : }
490 :
491 0 : if (ad->ad_rsrc_data == NULL) {
492 : /*
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.
497 : */
498 0 : ad->ad_rsrc_data = talloc_size(ad, reso_len);
499 0 : if (ad->ad_rsrc_data == NULL) {
500 0 : return false;
501 : }
502 :
503 0 : n = SMB_VFS_NEXT_PREAD(handle,
504 : fsp,
505 : ad->ad_rsrc_data,
506 : reso_len,
507 : ADEDOFF_RFORK_DOT_UND);
508 0 : if (n != reso_len) {
509 0 : DBG_ERR("Read on [%s] failed\n",
510 : fsp_str_dbg(fsp));
511 0 : ok = false;
512 0 : goto out;
513 : }
514 : }
515 :
516 0 : n = SMB_VFS_NEXT_PWRITE(handle,
517 : fsp,
518 : ad->ad_rsrc_data,
519 : reso_len,
520 : reso_off);
521 0 : if (n != reso_len) {
522 0 : DBG_ERR("Write on [%s] failed\n",
523 : fsp_str_dbg(fsp));
524 0 : ok = false;
525 0 : goto out;
526 : }
527 :
528 0 : ok = true;
529 0 : out:
530 0 : return ok;
531 : }
532 :
533 0 : static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
534 : struct adouble *ad,
535 : files_struct *fsp)
536 : {
537 0 : 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;
543 :
544 0 : if (ad->adx_entries == NULL) {
545 : /* No xattrs, nothing to pack */
546 0 : return true;
547 : }
548 :
549 0 : if (fsp == NULL) {
550 0 : DBG_ERR("fsp unexpectedly NULL\n");
551 0 : return false;
552 : }
553 :
554 0 : oldsize = talloc_get_size(ad->ad_data);
555 0 : if (oldsize < AD_XATTR_MAX_HDR_SIZE) {
556 0 : ad->ad_data = talloc_realloc(ad,
557 : ad->ad_data,
558 : char,
559 : AD_XATTR_MAX_HDR_SIZE);
560 0 : if (ad->ad_data == NULL) {
561 0 : return false;
562 : }
563 0 : memset(ad->ad_data + oldsize,
564 : 0,
565 : AD_XATTR_MAX_HDR_SIZE - oldsize);
566 : }
567 :
568 : /*
569 : * First, let's calculate the start of the xattr data area which will be
570 : * after the xattr header + header entries.
571 : */
572 :
573 0 : data_off = ad_getentryoff(ad, ADEID_FINDERI);
574 0 : data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
575 : /* 2 bytes padding */
576 0 : data_off += 2;
577 :
578 0 : for (i = 0; i < h->adx_num_attrs; i++) {
579 0 : struct ad_xattr_entry *e = &ad->adx_entries[i];
580 :
581 : /* Align on 4 byte boundary */
582 0 : data_off = (data_off + 3) & ~3;
583 :
584 0 : data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
585 0 : if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
586 0 : return false;
587 : }
588 : }
589 :
590 0 : off = ad_getentryoff(ad, ADEID_FINDERI);
591 0 : off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
592 : /* 2 bytes padding */
593 0 : off += 2;
594 :
595 0 : for (i = 0; i < h->adx_num_attrs; i++) {
596 0 : struct ad_xattr_entry *e = &ad->adx_entries[i];
597 :
598 : /* Align on 4 byte boundary */
599 0 : off = (off + 3) & ~3;
600 :
601 0 : e->adx_offset = data_off;
602 0 : data_off += e->adx_length;
603 :
604 0 : 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);
612 :
613 0 : if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
614 0 : return false;
615 : }
616 0 : RSIVAL(ad->ad_data, off, e->adx_offset);
617 0 : off += 4;
618 :
619 0 : if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
620 0 : return false;
621 : }
622 0 : RSIVAL(ad->ad_data, off, e->adx_length);
623 0 : off += 4;
624 :
625 0 : if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
626 0 : return false;
627 : }
628 0 : RSSVAL(ad->ad_data, off, e->adx_flags);
629 0 : off += 2;
630 :
631 0 : if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
632 0 : return false;
633 : }
634 0 : SCVAL(ad->ad_data, off, e->adx_namelen);
635 0 : off += 1;
636 :
637 0 : if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
638 0 : return false;
639 : }
640 0 : memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
641 0 : off += e->adx_namelen;
642 : }
643 :
644 0 : h->adx_data_start = off;
645 0 : h->adx_data_length = talloc_get_size(ad->adx_data);
646 0 : h->adx_total_size = h->adx_data_start + h->adx_data_length;
647 :
648 0 : if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
649 0 : ad->ad_data = talloc_realloc(ad,
650 : ad->ad_data,
651 : char,
652 : h->adx_total_size);
653 0 : if (ad->ad_data == NULL) {
654 0 : return false;
655 : }
656 : }
657 :
658 0 : memcpy(ad->ad_data + h->adx_data_start,
659 0 : ad->adx_data,
660 0 : h->adx_data_length);
661 :
662 0 : ad_setentrylen(ad,
663 : ADEID_FINDERI,
664 0 : h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
665 :
666 0 : ad_setentryoff(ad,
667 : ADEID_RFORK,
668 0 : ad_getentryoff(ad, ADEID_FINDERI) +
669 0 : ad_getentrylen(ad, ADEID_FINDERI));
670 :
671 0 : memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
672 :
673 : /*
674 : * Rewind, then update the header fields.
675 : */
676 :
677 0 : off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
678 : /* 2 bytes padding */
679 0 : off += 2;
680 :
681 0 : RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
682 0 : off += 4;
683 0 : RSIVAL(ad->ad_data, off, 0);
684 0 : off += 4;
685 0 : RSIVAL(ad->ad_data, off, h->adx_total_size);
686 0 : off += 4;
687 0 : RSIVAL(ad->ad_data, off, h->adx_data_start);
688 0 : off += 4;
689 0 : RSIVAL(ad->ad_data, off, h->adx_data_length);
690 0 : off += 4;
691 :
692 : /* adx_reserved and adx_flags */
693 0 : memset(ad->ad_data + off, 0, 3 * 4 + 2);
694 0 : off += 3 * 4 + 2;
695 :
696 0 : RSSVAL(ad->ad_data, off, h->adx_num_attrs);
697 0 : off += 2;
698 :
699 0 : ok = ad_pack_move_reso(handle, ad, fsp);
700 0 : if (!ok) {
701 0 : DBG_ERR("Moving resourcefork of [%s] failed\n",
702 : fsp_str_dbg(fsp));
703 0 : return false;
704 : }
705 :
706 0 : return true;
707 : }
708 :
709 : /**
710 : * Pack AppleDouble structure into data buffer
711 : **/
712 0 : static bool ad_pack(struct vfs_handle_struct *handle,
713 : struct adouble *ad,
714 : files_struct *fsp)
715 : {
716 : uint32_t eid;
717 : uint16_t nent;
718 : uint32_t bufsize;
719 0 : uint32_t offset = 0;
720 : bool ok;
721 :
722 0 : bufsize = talloc_get_size(ad->ad_data);
723 0 : if (bufsize < AD_DATASZ_DOT_UND) {
724 0 : DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
725 0 : return false;
726 : }
727 :
728 0 : if (offset + ADEDLEN_MAGIC < offset ||
729 0 : offset + ADEDLEN_MAGIC >= bufsize) {
730 0 : return false;
731 : }
732 0 : RSIVAL(ad->ad_data, offset, ad->ad_magic);
733 0 : offset += ADEDLEN_MAGIC;
734 :
735 0 : if (offset + ADEDLEN_VERSION < offset ||
736 0 : offset + ADEDLEN_VERSION >= bufsize) {
737 0 : return false;
738 : }
739 0 : RSIVAL(ad->ad_data, offset, ad->ad_version);
740 0 : offset += ADEDLEN_VERSION;
741 :
742 0 : if (offset + ADEDLEN_FILLER < offset ||
743 0 : offset + ADEDLEN_FILLER >= bufsize) {
744 0 : return false;
745 : }
746 0 : if (ad->ad_type == ADOUBLE_RSRC) {
747 0 : memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
748 : }
749 0 : offset += ADEDLEN_FILLER;
750 :
751 0 : if (offset + ADEDLEN_NENTRIES < offset ||
752 0 : offset + ADEDLEN_NENTRIES >= bufsize) {
753 0 : return false;
754 : }
755 0 : offset += ADEDLEN_NENTRIES;
756 :
757 0 : ok = ad_pack_xattrs(handle, ad, fsp);
758 0 : if (!ok) {
759 0 : return false;
760 : }
761 :
762 0 : for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
763 0 : if (ad->ad_eid[eid].ade_off == 0) {
764 : /*
765 : * ade_off is also used as indicator whether a
766 : * specific entry is used or not
767 : */
768 0 : continue;
769 : }
770 :
771 0 : if (offset + AD_ENTRY_LEN_EID < offset ||
772 0 : offset + AD_ENTRY_LEN_EID >= bufsize) {
773 0 : return false;
774 : }
775 0 : RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
776 0 : offset += AD_ENTRY_LEN_EID;
777 :
778 0 : if (offset + AD_ENTRY_LEN_OFF < offset ||
779 0 : offset + AD_ENTRY_LEN_OFF >= bufsize) {
780 0 : return false;
781 : }
782 0 : RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
783 0 : offset += AD_ENTRY_LEN_OFF;
784 :
785 0 : if (offset + AD_ENTRY_LEN_LEN < offset ||
786 0 : offset + AD_ENTRY_LEN_LEN >= bufsize) {
787 0 : return false;
788 : }
789 0 : RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
790 0 : offset += AD_ENTRY_LEN_LEN;
791 :
792 0 : nent++;
793 : }
794 :
795 0 : if (ADEDOFF_NENTRIES + 2 >= bufsize) {
796 0 : return false;
797 : }
798 0 : RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
799 :
800 0 : return true;
801 : }
802 :
803 0 : static bool ad_unpack_xattrs(struct adouble *ad)
804 : {
805 0 : struct ad_xattr_header *h = &ad->adx_header;
806 0 : size_t bufsize = talloc_get_size(ad->ad_data);
807 0 : const char *p = ad->ad_data;
808 : uint32_t hoff;
809 : uint32_t i;
810 :
811 0 : if (ad->ad_type != ADOUBLE_RSRC) {
812 0 : return false;
813 : }
814 :
815 0 : if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
816 0 : return true;
817 : }
818 :
819 : /*
820 : * Ensure the buffer ad->ad_data was allocated by ad_alloc() for an
821 : * ADOUBLE_RSRC type (._ AppleDouble file on-disk).
822 : */
823 0 : if (bufsize != AD_XATTR_MAX_HDR_SIZE) {
824 0 : return false;
825 : }
826 :
827 : /* 2 bytes padding */
828 0 : hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
829 :
830 0 : h->adx_magic = RIVAL(p, hoff + 0);
831 0 : h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
832 0 : h->adx_total_size = RIVAL(p, hoff + 8);
833 0 : h->adx_data_start = RIVAL(p, hoff + 12);
834 0 : h->adx_data_length = RIVAL(p, hoff + 16);
835 0 : h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
836 0 : h->adx_num_attrs = RSVAL(p, hoff + 34);
837 :
838 0 : if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
839 0 : DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
840 0 : return false;
841 : }
842 :
843 0 : if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
844 0 : DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
845 0 : return false;
846 : }
847 0 : if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
848 0 : DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
849 0 : return false;
850 : }
851 :
852 0 : if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
853 0 : DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
854 0 : return false;
855 : }
856 :
857 0 : if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
858 0 : DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
859 0 : return false;
860 : }
861 0 : if ((h->adx_data_start + h->adx_data_length) >
862 0 : ad->adx_header.adx_total_size)
863 : {
864 0 : DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
865 0 : return false;
866 : }
867 :
868 0 : if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
869 0 : DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
870 0 : return false;
871 : }
872 :
873 0 : if (h->adx_num_attrs == 0) {
874 0 : return true;
875 : }
876 :
877 0 : ad->adx_entries = talloc_zero_array(
878 : ad, struct ad_xattr_entry, h->adx_num_attrs);
879 0 : if (ad->adx_entries == NULL) {
880 0 : return false;
881 : }
882 :
883 0 : hoff += AD_XATTR_HDR_SIZE;
884 :
885 0 : for (i = 0; i < h->adx_num_attrs; i++) {
886 0 : struct ad_xattr_entry *e = &ad->adx_entries[i];
887 :
888 0 : hoff = (hoff + 3) & ~3;
889 :
890 0 : e->adx_offset = RIVAL(p, hoff + 0);
891 0 : e->adx_length = RIVAL(p, hoff + 4);
892 0 : e->adx_flags = RSVAL(p, hoff + 8);
893 0 : e->adx_namelen = *(p + hoff + 10);
894 :
895 0 : if (e->adx_offset >= ad->adx_header.adx_total_size) {
896 0 : DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
897 : e->adx_offset);
898 0 : return false;
899 : }
900 :
901 0 : if ((e->adx_offset + e->adx_length) < e->adx_offset) {
902 0 : DBG_ERR("Bad adx_length: %" PRIx32 "\n",
903 : e->adx_length);
904 0 : return false;
905 : }
906 :
907 0 : if ((e->adx_offset + e->adx_length) >
908 0 : ad->adx_header.adx_total_size)
909 : {
910 0 : DBG_ERR("Bad adx_length: %" PRIx32 "\n",
911 : e->adx_length);
912 0 : return false;
913 : }
914 :
915 0 : if (e->adx_namelen == 0) {
916 0 : DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
917 : e->adx_namelen);
918 0 : return false;
919 : }
920 0 : if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
921 0 : DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
922 : e->adx_namelen);
923 0 : return false;
924 : }
925 0 : if ((hoff + 11 + e->adx_namelen) >
926 0 : ad->adx_header.adx_data_start)
927 : {
928 0 : DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
929 : e->adx_namelen);
930 0 : return false;
931 : }
932 :
933 0 : e->adx_name = talloc_strndup(ad->adx_entries,
934 0 : p + hoff + 11,
935 0 : e->adx_namelen);
936 0 : if (e->adx_name == NULL) {
937 0 : return false;
938 : }
939 :
940 0 : DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
941 : e->adx_name, e->adx_offset, e->adx_length);
942 0 : dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
943 0 : e->adx_length);
944 :
945 0 : hoff += 11 + e->adx_namelen;
946 : }
947 :
948 0 : return true;
949 : }
950 :
951 : /**
952 : * Unpack an AppleDouble blob into a struct adoble
953 : **/
954 0 : static bool ad_unpack(struct adouble *ad, const size_t nentries,
955 : size_t filesize)
956 : {
957 0 : size_t bufsize = talloc_get_size(ad->ad_data);
958 : size_t adentries, i;
959 : uint32_t eid, len, off;
960 : bool ok;
961 :
962 : /*
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.
967 : */
968 :
969 0 : if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
970 0 : DEBUG(1, ("bad size\n"));
971 0 : return false;
972 : }
973 :
974 0 : ad->ad_magic = RIVAL(ad->ad_data, 0);
975 0 : ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
976 0 : if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
977 0 : DEBUG(1, ("wrong magic or version\n"));
978 0 : return false;
979 : }
980 :
981 0 : memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
982 :
983 0 : adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
984 0 : if (adentries != nentries) {
985 0 : DEBUG(1, ("invalid number of entries: %zu\n",
986 : adentries));
987 0 : return false;
988 : }
989 :
990 : /* now, read in the entry bits */
991 0 : for (i = 0; i < adentries; i++) {
992 0 : eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
993 0 : eid = get_eid(eid);
994 0 : off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
995 0 : len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
996 :
997 0 : if (!eid || eid >= ADEID_MAX) {
998 0 : DEBUG(1, ("bogus eid %d\n", eid));
999 0 : return false;
1000 : }
1001 :
1002 : /*
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
1006 : */
1007 0 : if ((off > bufsize) && (eid != ADEID_RFORK)) {
1008 0 : DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
1009 : eid, off, len));
1010 0 : return false;
1011 : }
1012 :
1013 0 : ok = ad_entry_check_size(eid, bufsize, off, len);
1014 0 : if (!ok) {
1015 0 : DBG_ERR("bogus eid [%"PRIu32"] bufsize [%zu] "
1016 : "off [%"PRIu32"] len [%"PRIu32"]\n",
1017 : eid, bufsize, off, len);
1018 0 : return false;
1019 : }
1020 :
1021 : /*
1022 : * That would be obviously broken
1023 : */
1024 0 : if (off > filesize) {
1025 0 : DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
1026 : eid, off, len));
1027 0 : return false;
1028 : }
1029 :
1030 : /*
1031 : * Check for any entry that has its end beyond the
1032 : * filesize.
1033 : */
1034 0 : if (off + len < off) {
1035 0 : DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
1036 : ", len: %" PRIu32 "\n",
1037 : eid, off, len));
1038 0 : return false;
1039 :
1040 : }
1041 0 : if (off + len > filesize) {
1042 : /*
1043 : * If this is the resource fork entry, we fix
1044 : * up the length, for any other entry we bail
1045 : * out.
1046 : */
1047 0 : if (eid != ADEID_RFORK) {
1048 0 : DEBUG(1, ("bogus eid %d: off: %" PRIu32
1049 : ", len: %" PRIu32 "\n",
1050 : eid, off, len));
1051 0 : return false;
1052 : }
1053 :
1054 : /*
1055 : * Fixup the resource fork entry by limiting
1056 : * the size to entryoffset - filesize.
1057 : */
1058 0 : len = filesize - off;
1059 0 : DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
1060 : ", len: %" PRIu32 "\n", off, len));
1061 : }
1062 :
1063 0 : ad->ad_eid[eid].ade_off = off;
1064 0 : ad->ad_eid[eid].ade_len = len;
1065 : }
1066 :
1067 0 : if (ad->ad_type == ADOUBLE_RSRC) {
1068 0 : ok = ad_unpack_xattrs(ad);
1069 0 : if (!ok) {
1070 0 : return false;
1071 : }
1072 : }
1073 :
1074 0 : return true;
1075 : }
1076 :
1077 0 : static bool ad_convert_move_reso(vfs_handle_struct *handle,
1078 : struct adouble *ad,
1079 : const struct smb_filename *smb_fname)
1080 : {
1081 0 : char *buf = NULL;
1082 : size_t rforklen;
1083 : size_t rforkoff;
1084 : ssize_t n;
1085 : int ret;
1086 :
1087 0 : rforklen = ad_getentrylen(ad, ADEID_RFORK);
1088 0 : if (rforklen == 0) {
1089 0 : return true;
1090 : }
1091 :
1092 0 : buf = talloc_size(ad, rforklen);
1093 0 : if (buf == NULL) {
1094 : /*
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.
1099 : */
1100 0 : DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1101 : rforklen);
1102 0 : return false;
1103 : }
1104 :
1105 0 : rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1106 :
1107 0 : n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1108 0 : if (n != rforklen) {
1109 0 : DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1110 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1111 0 : return false;
1112 : }
1113 :
1114 0 : rforkoff = ADEDOFF_RFORK_DOT_UND;
1115 :
1116 0 : n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1117 0 : if (n != rforklen) {
1118 0 : DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1119 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1120 0 : return false;
1121 : }
1122 :
1123 0 : ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1124 :
1125 0 : ret = ad_fset(handle, ad, ad->ad_fsp);
1126 0 : if (ret != 0) {
1127 0 : DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1128 0 : return false;
1129 : }
1130 :
1131 0 : return true;
1132 : }
1133 :
1134 0 : 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)
1139 : {
1140 : static struct char_mappings **string_replace_cmaps = NULL;
1141 : uint16_t i;
1142 0 : int saved_errno = 0;
1143 : NTSTATUS status;
1144 : int rc;
1145 : bool ok;
1146 :
1147 0 : *converted_xattr = false;
1148 :
1149 0 : if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1150 0 : return true;
1151 : }
1152 :
1153 0 : if (string_replace_cmaps == NULL) {
1154 0 : const char **mappings = NULL;
1155 :
1156 0 : mappings = str_list_make_v3_const(
1157 : talloc_tos(), catia_mappings, NULL);
1158 0 : if (mappings == NULL) {
1159 0 : return false;
1160 : }
1161 0 : string_replace_cmaps = string_replace_init_map(
1162 0 : handle->conn->sconn, mappings);
1163 0 : TALLOC_FREE(mappings);
1164 : }
1165 :
1166 0 : for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1167 0 : struct ad_xattr_entry *e = &ad->adx_entries[i];
1168 0 : char *mapped_name = NULL;
1169 0 : char *tmp = NULL;
1170 0 : struct smb_filename *stream_name = NULL;
1171 0 : files_struct *fsp = NULL;
1172 : ssize_t nwritten;
1173 :
1174 0 : status = string_replace_allocate(handle->conn,
1175 0 : e->adx_name,
1176 : string_replace_cmaps,
1177 : talloc_tos(),
1178 : &mapped_name,
1179 : vfs_translate_to_windows);
1180 0 : if (!NT_STATUS_IS_OK(status) &&
1181 0 : !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1182 : {
1183 0 : DBG_ERR("string_replace_allocate failed\n");
1184 0 : ok = false;
1185 0 : goto fail;
1186 : }
1187 :
1188 0 : tmp = mapped_name;
1189 0 : mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1190 0 : TALLOC_FREE(tmp);
1191 0 : if (mapped_name == NULL) {
1192 0 : ok = false;
1193 0 : goto fail;
1194 : }
1195 :
1196 0 : stream_name = synthetic_smb_fname(talloc_tos(),
1197 0 : smb_fname->base_name,
1198 : mapped_name,
1199 : NULL,
1200 0 : smb_fname->twrp,
1201 0 : smb_fname->flags);
1202 0 : TALLOC_FREE(mapped_name);
1203 0 : if (stream_name == NULL) {
1204 0 : DBG_ERR("synthetic_smb_fname failed\n");
1205 0 : ok = false;
1206 0 : goto fail;
1207 : }
1208 :
1209 0 : DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1210 :
1211 0 : status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1212 0 : if (!NT_STATUS_IS_OK(status) &&
1213 0 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1214 : {
1215 0 : ok = false;
1216 0 : goto fail;
1217 : }
1218 :
1219 0 : 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 | FILE_SHARE_DELETE, /* 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 0 : TALLOC_FREE(stream_name);
1239 0 : if (!NT_STATUS_IS_OK(status)) {
1240 0 : DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1241 0 : ok = false;
1242 0 : goto fail;
1243 : }
1244 :
1245 0 : nwritten = SMB_VFS_PWRITE(fsp,
1246 : ad->ad_data + e->adx_offset,
1247 : e->adx_length,
1248 : 0);
1249 0 : if (nwritten == -1) {
1250 0 : DBG_ERR("SMB_VFS_PWRITE failed\n");
1251 0 : saved_errno = errno;
1252 0 : close_file_free(NULL, &fsp, ERROR_CLOSE);
1253 0 : errno = saved_errno;
1254 0 : ok = false;
1255 0 : goto fail;
1256 : }
1257 :
1258 0 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1259 0 : if (!NT_STATUS_IS_OK(status)) {
1260 0 : ok = false;
1261 0 : goto fail;
1262 : }
1263 0 : fsp = NULL;
1264 : }
1265 :
1266 0 : ad->adx_header.adx_num_attrs = 0;
1267 0 : TALLOC_FREE(ad->adx_entries);
1268 :
1269 0 : ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1270 :
1271 0 : rc = ad_fset(handle, ad, ad->ad_fsp);
1272 0 : if (rc != 0) {
1273 0 : DBG_ERR("ad_fset on [%s] failed: %s\n",
1274 : fsp_str_dbg(ad->ad_fsp), strerror(errno));
1275 0 : ok = false;
1276 0 : goto fail;
1277 : }
1278 :
1279 0 : ok = ad_convert_move_reso(handle, ad, smb_fname);
1280 0 : if (!ok) {
1281 0 : goto fail;
1282 : }
1283 :
1284 0 : *converted_xattr = true;
1285 0 : ok = true;
1286 :
1287 0 : fail:
1288 0 : return ok;
1289 : }
1290 :
1291 0 : static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1292 : struct adouble *ad,
1293 : const struct smb_filename *smb_fname)
1294 : {
1295 0 : char *p_ad = NULL;
1296 0 : AfpInfo *ai = NULL;
1297 : DATA_BLOB aiblob;
1298 0 : struct smb_filename *stream_name = NULL;
1299 0 : files_struct *fsp = NULL;
1300 : size_t size;
1301 : ssize_t nwritten;
1302 : NTSTATUS status;
1303 0 : int saved_errno = 0;
1304 : int cmp;
1305 :
1306 0 : cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1307 0 : if (cmp != 0) {
1308 0 : return true;
1309 : }
1310 :
1311 0 : p_ad = ad_get_entry(ad, ADEID_FINDERI);
1312 0 : if (p_ad == NULL) {
1313 0 : return false;
1314 : }
1315 :
1316 0 : ai = afpinfo_new(talloc_tos());
1317 0 : if (ai == NULL) {
1318 0 : return false;
1319 : }
1320 :
1321 0 : memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1322 :
1323 0 : aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1324 0 : if (aiblob.data == NULL) {
1325 0 : TALLOC_FREE(ai);
1326 0 : return false;
1327 : }
1328 :
1329 0 : size = afpinfo_pack(ai, (char *)aiblob.data);
1330 0 : TALLOC_FREE(ai);
1331 0 : if (size != AFP_INFO_SIZE) {
1332 0 : return false;
1333 : }
1334 :
1335 0 : stream_name = synthetic_smb_fname(talloc_tos(),
1336 0 : smb_fname->base_name,
1337 : AFPINFO_STREAM,
1338 : NULL,
1339 0 : smb_fname->twrp,
1340 0 : smb_fname->flags);
1341 0 : if (stream_name == NULL) {
1342 0 : data_blob_free(&aiblob);
1343 0 : DBG_ERR("synthetic_smb_fname failed\n");
1344 0 : return false;
1345 : }
1346 :
1347 0 : DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1348 :
1349 0 : status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1350 0 : if (!NT_STATUS_IS_OK(status) &&
1351 0 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1352 : {
1353 0 : return false;
1354 : }
1355 :
1356 0 : 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 0 : TALLOC_FREE(stream_name);
1376 0 : if (!NT_STATUS_IS_OK(status)) {
1377 0 : DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1378 0 : return false;
1379 : }
1380 :
1381 0 : nwritten = SMB_VFS_PWRITE(fsp,
1382 : aiblob.data,
1383 : aiblob.length,
1384 : 0);
1385 0 : if (nwritten == -1) {
1386 0 : DBG_ERR("SMB_VFS_PWRITE failed\n");
1387 0 : saved_errno = errno;
1388 0 : close_file_free(NULL, &fsp, ERROR_CLOSE);
1389 0 : errno = saved_errno;
1390 0 : return false;
1391 : }
1392 :
1393 0 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1394 0 : if (!NT_STATUS_IS_OK(status)) {
1395 0 : return false;
1396 : }
1397 0 : fsp = NULL;
1398 :
1399 0 : return true;
1400 : }
1401 :
1402 0 : static bool ad_convert_truncate(vfs_handle_struct *handle,
1403 : struct adouble *ad,
1404 : const struct smb_filename *smb_fname)
1405 : {
1406 : int rc;
1407 : off_t newlen;
1408 :
1409 0 : newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1410 :
1411 0 : rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1412 0 : if (rc != 0) {
1413 0 : return false;
1414 : }
1415 :
1416 0 : return true;
1417 : }
1418 :
1419 0 : static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1420 : struct adouble *ad,
1421 : uint32_t flags,
1422 : bool *blank)
1423 0 : {
1424 0 : size_t rforklen = sizeof(empty_resourcefork);
1425 0 : char buf[rforklen];
1426 : ssize_t nread;
1427 : int cmp;
1428 : int rc;
1429 :
1430 0 : *blank = false;
1431 :
1432 0 : if (!(flags & AD_CONV_WIPE_BLANK)) {
1433 0 : return true;
1434 : }
1435 :
1436 0 : if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1437 0 : return true;
1438 : }
1439 :
1440 0 : nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1441 0 : if (nread != rforklen) {
1442 0 : DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1443 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1444 0 : return false;
1445 : }
1446 :
1447 0 : cmp = memcmp(buf, empty_resourcefork, rforklen);
1448 0 : if (cmp != 0) {
1449 0 : return true;
1450 : }
1451 :
1452 0 : ad_setentrylen(ad, ADEID_RFORK, 0);
1453 :
1454 0 : rc = ad_fset(handle, ad, ad->ad_fsp);
1455 0 : if (rc != 0) {
1456 0 : DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1457 0 : return false;
1458 : }
1459 :
1460 0 : *blank = true;
1461 0 : return true;
1462 : }
1463 :
1464 0 : 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)
1468 : {
1469 0 : struct smb_filename *parent_fname = NULL;
1470 0 : struct smb_filename *at_fname = NULL;
1471 0 : struct smb_filename *ad_name = NULL;
1472 : NTSTATUS status;
1473 : int rc;
1474 :
1475 0 : if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1476 0 : return true;
1477 : }
1478 :
1479 0 : if (!(flags & AD_CONV_DELETE)) {
1480 0 : return true;
1481 : }
1482 :
1483 0 : rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1484 0 : if (rc != 0) {
1485 0 : return false;
1486 : }
1487 :
1488 0 : status = parent_pathref(talloc_tos(),
1489 0 : handle->conn->cwd_fsp,
1490 : ad_name,
1491 : &parent_fname,
1492 : &at_fname);
1493 0 : TALLOC_FREE(ad_name);
1494 0 : if (!NT_STATUS_IS_OK(status)) {
1495 0 : return false;
1496 : }
1497 :
1498 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
1499 : parent_fname->fsp,
1500 : at_fname,
1501 : 0);
1502 0 : if (rc != 0) {
1503 0 : 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 0 : TALLOC_FREE(parent_fname);
1507 0 : return false;
1508 : }
1509 :
1510 0 : DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1511 : smb_fname_str_dbg(parent_fname),
1512 : smb_fname_str_dbg(at_fname));
1513 0 : TALLOC_FREE(parent_fname);
1514 :
1515 0 : return true;
1516 : }
1517 :
1518 : /**
1519 : * Convert from Apple's ._ file to Netatalk
1520 : *
1521 : * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1522 : * bytes containing packed xattrs.
1523 : *
1524 : * @return -1 in case an error occurred, 0 if no conversion was done, 1
1525 : * otherwise
1526 : **/
1527 0 : int ad_convert(struct vfs_handle_struct *handle,
1528 : const struct smb_filename *smb_fname,
1529 : const char *catia_mappings,
1530 : uint32_t flags)
1531 : {
1532 0 : struct adouble *ad = NULL;
1533 : bool ok;
1534 0 : bool converted_xattr = false;
1535 : bool blank;
1536 : int ret;
1537 :
1538 0 : ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1539 0 : if (ad == NULL) {
1540 0 : return 0;
1541 : }
1542 :
1543 0 : ok = ad_convert_xattr(handle,
1544 : ad,
1545 : smb_fname,
1546 : catia_mappings,
1547 : &converted_xattr);
1548 0 : if (!ok) {
1549 0 : ret = -1;
1550 0 : goto done;
1551 : }
1552 :
1553 0 : ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1554 0 : if (!ok) {
1555 0 : ret = -1;
1556 0 : goto done;
1557 : }
1558 :
1559 0 : if (converted_xattr || blank) {
1560 0 : ok = ad_convert_truncate(handle, ad, smb_fname);
1561 0 : if (!ok) {
1562 0 : ret = -1;
1563 0 : goto done;
1564 : }
1565 : }
1566 :
1567 0 : ok = ad_convert_finderinfo(handle, ad, smb_fname);
1568 0 : if (!ok) {
1569 0 : DBG_ERR("Failed to convert [%s]\n",
1570 : smb_fname_str_dbg(smb_fname));
1571 0 : ret = -1;
1572 0 : goto done;
1573 : }
1574 :
1575 0 : ok = ad_convert_delete_adfile(handle,
1576 : ad,
1577 : smb_fname,
1578 : flags);
1579 0 : if (!ok) {
1580 0 : ret = -1;
1581 0 : goto done;
1582 : }
1583 :
1584 0 : ret = 0;
1585 0 : done:
1586 0 : TALLOC_FREE(ad);
1587 0 : return ret;
1588 : }
1589 :
1590 0 : 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)
1595 : {
1596 0 : files_struct *fsp = NULL;
1597 : NTSTATUS status;
1598 : int ret;
1599 :
1600 0 : status = openat_pathref_fsp(handle->conn->cwd_fsp, adpath);
1601 0 : if (!NT_STATUS_IS_OK(status) &&
1602 0 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1603 0 : return false;
1604 : }
1605 :
1606 0 : 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 0 : if (!NT_STATUS_IS_OK(status)) {
1626 0 : DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1627 : smb_fname_str_dbg(adpath), nt_errstr(status));
1628 0 : return false;
1629 : }
1630 :
1631 0 : if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1632 0 : fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1633 : {
1634 0 : ret = SMB_VFS_FCHOWN(fsp,
1635 : smb_fname->st.st_ex_uid,
1636 : smb_fname->st.st_ex_gid);
1637 0 : if (ret != 0) {
1638 0 : DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1639 : fsp_str_dbg(fsp), nt_errstr(status));
1640 0 : close_file_free(NULL, &fsp, NORMAL_CLOSE);
1641 0 : return false;
1642 : }
1643 : }
1644 :
1645 0 : *_fsp = fsp;
1646 0 : return true;
1647 : }
1648 :
1649 0 : 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)
1654 : {
1655 0 : files_struct *fsp = NULL;
1656 : NTSTATUS status;
1657 :
1658 0 : status = openat_pathref_fsp(handle->conn->cwd_fsp, smb_fname);
1659 0 : if (!NT_STATUS_IS_OK(status)) {
1660 0 : return false;
1661 : }
1662 :
1663 0 : 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 0 : if (!NT_STATUS_IS_OK(status)) {
1684 0 : DBG_ERR("Opening [%s] failed: %s\n",
1685 : smb_fname_str_dbg(smb_fname),
1686 : nt_errstr(status));
1687 0 : return false;
1688 : }
1689 :
1690 0 : status = vfs_fstreaminfo(fsp,
1691 : mem_ctx,
1692 : num_streams,
1693 : streams);
1694 0 : if (!NT_STATUS_IS_OK(status)) {
1695 0 : close_file_free(NULL, &fsp, NORMAL_CLOSE);
1696 0 : DBG_ERR("streaminfo on [%s] failed: %s\n",
1697 : smb_fname_str_dbg(smb_fname),
1698 : nt_errstr(status));
1699 0 : return false;
1700 : }
1701 :
1702 0 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1703 0 : if (!NT_STATUS_IS_OK(status)) {
1704 0 : DBG_ERR("close_file [%s] failed: %s\n",
1705 : smb_fname_str_dbg(smb_fname),
1706 : nt_errstr(status));
1707 0 : return false;
1708 : }
1709 :
1710 0 : return true;
1711 : }
1712 :
1713 : struct ad_collect_state {
1714 : bool have_adfile;
1715 : size_t adx_data_off;
1716 : char *rsrc_data_buf;
1717 : };
1718 :
1719 0 : 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)
1725 : {
1726 0 : struct smb_filename *sname = NULL;
1727 0 : files_struct *fsp = NULL;
1728 0 : struct ad_xattr_entry *e = NULL;
1729 0 : char *mapped_name = NULL;
1730 0 : char *p = NULL;
1731 : size_t needed_size;
1732 : ssize_t nread;
1733 : NTSTATUS status;
1734 : bool ok;
1735 :
1736 0 : sname = synthetic_smb_fname(ad,
1737 0 : smb_fname->base_name,
1738 0 : stream->name,
1739 : NULL,
1740 : smb_fname->twrp,
1741 : 0);
1742 0 : if (sname == NULL) {
1743 0 : return false;
1744 : }
1745 :
1746 0 : if (is_ntfs_default_stream_smb_fname(sname)) {
1747 0 : TALLOC_FREE(sname);
1748 0 : return true;
1749 : }
1750 :
1751 0 : DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1752 :
1753 0 : status = openat_pathref_fsp(handle->conn->cwd_fsp, sname);
1754 0 : if (!NT_STATUS_IS_OK(status)) {
1755 0 : ok = false;
1756 0 : goto out;
1757 : }
1758 :
1759 0 : 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 0 : if (!NT_STATUS_IS_OK(status)) {
1779 0 : DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1780 : smb_fname_str_dbg(sname));
1781 0 : ok = false;
1782 0 : goto out;
1783 : }
1784 :
1785 0 : if (is_afpinfo_stream(stream->name)) {
1786 : char buf[AFP_INFO_SIZE];
1787 :
1788 0 : if (stream->size != AFP_INFO_SIZE) {
1789 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1790 : (ssize_t)stream->size,
1791 : smb_fname_str_dbg(sname));
1792 0 : ok = false;
1793 0 : goto out;
1794 : }
1795 :
1796 0 : nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1797 0 : if (nread != AFP_INFO_SIZE) {
1798 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1799 : (ssize_t)stream->size,
1800 : smb_fname_str_dbg(sname));
1801 0 : ok = false;
1802 0 : goto out;
1803 : }
1804 :
1805 0 : memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1806 : buf + AFP_OFF_FinderInfo,
1807 : AFP_FinderSize);
1808 :
1809 0 : ok = set_delete_on_close(fsp,
1810 : true,
1811 0 : fsp->conn->session_info->security_token,
1812 0 : fsp->conn->session_info->unix_token);
1813 0 : if (!ok) {
1814 0 : DBG_ERR("Deleting [%s] failed\n",
1815 : smb_fname_str_dbg(sname));
1816 0 : ok = false;
1817 0 : goto out;
1818 : }
1819 0 : ok = true;
1820 0 : goto out;
1821 : }
1822 :
1823 0 : if (is_afpresource_stream(stream->name)) {
1824 0 : ad->ad_rsrc_data = talloc_size(ad, stream->size);
1825 0 : if (ad->ad_rsrc_data == NULL) {
1826 0 : ok = false;
1827 0 : goto out;
1828 : }
1829 :
1830 0 : nread = SMB_VFS_PREAD(fsp,
1831 : ad->ad_rsrc_data,
1832 : stream->size,
1833 : 0);
1834 0 : if (nread != stream->size) {
1835 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1836 : (ssize_t)stream->size,
1837 : smb_fname_str_dbg(sname));
1838 0 : ok = false;
1839 0 : goto out;
1840 : }
1841 :
1842 0 : ad_setentrylen(ad, ADEID_RFORK, stream->size);
1843 :
1844 0 : if (!state->have_adfile) {
1845 : /*
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.
1850 : */
1851 0 : ok = set_delete_on_close(
1852 : fsp,
1853 : true,
1854 0 : fsp->conn->session_info->security_token,
1855 0 : fsp->conn->session_info->unix_token);
1856 0 : if (!ok) {
1857 0 : DBG_ERR("Deleting [%s] failed\n",
1858 : smb_fname_str_dbg(sname));
1859 0 : ok = false;
1860 0 : goto out;
1861 : }
1862 : }
1863 0 : ok = true;
1864 0 : goto out;
1865 : }
1866 :
1867 0 : ad->adx_entries = talloc_realloc(ad,
1868 : ad->adx_entries,
1869 : struct ad_xattr_entry,
1870 : ad->adx_header.adx_num_attrs + 1);
1871 0 : if (ad->adx_entries == NULL) {
1872 0 : ok = false;
1873 0 : goto out;
1874 : }
1875 :
1876 0 : e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1877 0 : *e = (struct ad_xattr_entry) {
1878 0 : .adx_length = stream->size,
1879 : };
1880 0 : e->adx_name = talloc_strdup(ad, stream->name + 1);
1881 0 : if (e->adx_name == NULL) {
1882 0 : ok = false;
1883 0 : goto out;
1884 : }
1885 0 : p = strchr(e->adx_name, ':');
1886 0 : if (p != NULL) {
1887 0 : *p = '\0';
1888 : }
1889 :
1890 0 : status = string_replace_allocate(handle->conn,
1891 0 : e->adx_name,
1892 : cmaps,
1893 : ad,
1894 : &mapped_name,
1895 : vfs_translate_to_unix);
1896 0 : if (!NT_STATUS_IS_OK(status) &&
1897 0 : !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1898 : {
1899 0 : DBG_ERR("string_replace_allocate failed\n");
1900 0 : ok = false;
1901 0 : goto out;
1902 : }
1903 :
1904 0 : e->adx_name = mapped_name;
1905 0 : e->adx_namelen = strlen(e->adx_name) + 1,
1906 :
1907 0 : 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);
1911 :
1912 0 : ad->adx_header.adx_num_attrs++;
1913 :
1914 0 : needed_size = state->adx_data_off + stream->size;
1915 0 : if (needed_size > talloc_get_size(ad->adx_data)) {
1916 0 : ad->adx_data = talloc_realloc(ad,
1917 : ad->adx_data,
1918 : char,
1919 : needed_size);
1920 0 : if (ad->adx_data == NULL) {
1921 0 : ok = false;
1922 0 : goto out;
1923 : }
1924 : }
1925 :
1926 0 : nread = SMB_VFS_PREAD(fsp,
1927 : ad->adx_data + state->adx_data_off,
1928 : stream->size,
1929 : 0);
1930 0 : if (nread != stream->size) {
1931 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1932 : (ssize_t)stream->size,
1933 : smb_fname_str_dbg(sname));
1934 0 : ok = false;
1935 0 : goto out;
1936 : }
1937 0 : state->adx_data_off += nread;
1938 :
1939 0 : ok = set_delete_on_close(fsp,
1940 : true,
1941 0 : fsp->conn->session_info->security_token,
1942 0 : fsp->conn->session_info->unix_token);
1943 0 : if (!ok) {
1944 0 : DBG_ERR("Deleting [%s] failed\n",
1945 : smb_fname_str_dbg(sname));
1946 0 : ok = false;
1947 0 : goto out;
1948 : }
1949 :
1950 0 : out:
1951 0 : TALLOC_FREE(sname);
1952 0 : if (fsp != NULL) {
1953 0 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1954 0 : if (!NT_STATUS_IS_OK(status)) {
1955 0 : DBG_ERR("close_file [%s] failed: %s\n",
1956 : smb_fname_str_dbg(smb_fname),
1957 : nt_errstr(status));
1958 0 : ok = false;
1959 : }
1960 : }
1961 :
1962 0 : return ok;
1963 : }
1964 :
1965 : /**
1966 : * Convert filesystem metadata to AppleDouble file
1967 : **/
1968 0 : 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)
1973 : {
1974 : static struct char_mappings **cmaps = NULL;
1975 0 : TALLOC_CTX *frame = talloc_stackframe();
1976 : struct ad_collect_state state;
1977 0 : struct stream_struct *streams = NULL;
1978 0 : struct smb_filename *adpath = NULL;
1979 0 : struct adouble *ad = NULL;
1980 0 : unsigned int num_streams = 0;
1981 0 : size_t to_convert = 0;
1982 0 : bool have_rsrc = false;
1983 0 : files_struct *fsp = NULL;
1984 : size_t i;
1985 : NTSTATUS status;
1986 : int ret;
1987 : bool ok;
1988 :
1989 0 : *converted = false;
1990 :
1991 0 : if (cmaps == NULL) {
1992 0 : const char **mappings = NULL;
1993 :
1994 0 : mappings = str_list_make_v3_const(
1995 : frame, catia_mappings, NULL);
1996 0 : if (mappings == NULL) {
1997 0 : ok = false;
1998 0 : goto out;
1999 : }
2000 0 : cmaps = string_replace_init_map(mem_ctx, mappings);
2001 0 : TALLOC_FREE(mappings);
2002 : }
2003 :
2004 0 : ok = ad_unconvert_get_streams(handle,
2005 : smb_fname,
2006 : frame,
2007 : &num_streams,
2008 : &streams);
2009 0 : if (!ok) {
2010 0 : goto out;
2011 : }
2012 :
2013 0 : for (i = 0; i < num_streams; i++) {
2014 0 : if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
2015 0 : continue;
2016 : }
2017 0 : to_convert++;
2018 0 : if (is_afpresource_stream(streams[i].name)) {
2019 0 : have_rsrc = true;
2020 : }
2021 : }
2022 :
2023 0 : if (to_convert == 0) {
2024 0 : ok = true;
2025 0 : goto out;
2026 : }
2027 :
2028 0 : state = (struct ad_collect_state) {
2029 : .adx_data_off = 0,
2030 : };
2031 :
2032 0 : ret = adouble_path(frame, smb_fname, &adpath);
2033 0 : if (ret != 0) {
2034 0 : ok = false;
2035 0 : goto out;
2036 : }
2037 :
2038 0 : ret = SMB_VFS_STAT(handle->conn, adpath);
2039 0 : if (ret == 0) {
2040 0 : state.have_adfile = true;
2041 : } else {
2042 0 : if (errno != ENOENT) {
2043 0 : ok = false;
2044 0 : goto out;
2045 : }
2046 0 : state.have_adfile = false;
2047 : }
2048 :
2049 0 : if (to_convert == 1 && have_rsrc && state.have_adfile) {
2050 : /*
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.
2054 : */
2055 0 : ok = true;
2056 0 : goto out;
2057 : }
2058 :
2059 0 : ad = ad_init(frame, ADOUBLE_RSRC);
2060 0 : if (ad == NULL) {
2061 0 : ok = false;
2062 0 : goto out;
2063 : }
2064 :
2065 0 : for (i = 0; i < num_streams; i++) {
2066 0 : ok = ad_collect_one_stream(handle,
2067 : cmaps,
2068 : smb_fname,
2069 0 : &streams[i],
2070 : ad,
2071 : &state);
2072 0 : if (!ok) {
2073 0 : goto out;
2074 : }
2075 : }
2076 :
2077 0 : ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
2078 0 : if (!ok) {
2079 0 : DBG_ERR("Failed to open adfile [%s]\n",
2080 : smb_fname_str_dbg(smb_fname));
2081 0 : goto out;
2082 : }
2083 :
2084 0 : ret = ad_fset(handle, ad, fsp);
2085 0 : if (ret != 0) {
2086 0 : ok = false;
2087 0 : goto out;
2088 : }
2089 :
2090 0 : *converted = true;
2091 0 : ok = true;
2092 :
2093 0 : out:
2094 0 : if (fsp != NULL) {
2095 0 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
2096 0 : if (!NT_STATUS_IS_OK(status)) {
2097 0 : DBG_ERR("close_file_free() [%s] failed: %s\n",
2098 : smb_fname_str_dbg(smb_fname),
2099 : nt_errstr(status));
2100 0 : ok = false;
2101 : }
2102 : }
2103 0 : TALLOC_FREE(frame);
2104 0 : return ok;
2105 : }
2106 :
2107 : /**
2108 : * Read and parse Netatalk AppleDouble metadata xattr
2109 : **/
2110 0 : static ssize_t ad_read_meta(vfs_handle_struct *handle,
2111 : struct adouble *ad,
2112 : const struct smb_filename *smb_fname)
2113 : {
2114 0 : int rc = 0;
2115 : ssize_t ealen;
2116 : bool ok;
2117 0 : struct files_struct *fsp = smb_fname->fsp;
2118 :
2119 0 : DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
2120 :
2121 0 : fsp = metadata_fsp(fsp);
2122 :
2123 0 : ealen = SMB_VFS_FGETXATTR(fsp,
2124 : AFPINFO_EA_NETATALK,
2125 : ad->ad_data,
2126 : AD_DATASZ_XATTR);
2127 :
2128 0 : if (ealen == -1) {
2129 0 : switch (errno) {
2130 0 : case ENOATTR:
2131 : case ENOENT:
2132 0 : if (errno == ENOATTR) {
2133 0 : errno = ENOENT;
2134 : }
2135 0 : rc = -1;
2136 0 : goto exit;
2137 0 : default:
2138 0 : DEBUG(2, ("error reading meta xattr: %s\n",
2139 : strerror(errno)));
2140 0 : rc = -1;
2141 0 : goto exit;
2142 : }
2143 : }
2144 0 : if (ealen != AD_DATASZ_XATTR) {
2145 0 : DEBUG(2, ("bad size %zd\n", ealen));
2146 0 : errno = EINVAL;
2147 0 : rc = -1;
2148 0 : goto exit;
2149 : }
2150 :
2151 : /* Now parse entries */
2152 0 : ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2153 0 : if (!ok) {
2154 0 : DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2155 0 : errno = EINVAL;
2156 0 : rc = -1;
2157 0 : goto exit;
2158 : }
2159 :
2160 0 : if (!ad_getentryoff(ad, ADEID_FINDERI)
2161 0 : || !ad_getentryoff(ad, ADEID_COMMENT)
2162 0 : || !ad_getentryoff(ad, ADEID_FILEDATESI)
2163 0 : || !ad_getentryoff(ad, ADEID_AFPFILEI)
2164 0 : || !ad_getentryoff(ad, ADEID_PRIVDEV)
2165 0 : || !ad_getentryoff(ad, ADEID_PRIVINO)
2166 0 : || !ad_getentryoff(ad, ADEID_PRIVSYN)
2167 0 : || !ad_getentryoff(ad, ADEID_PRIVID)) {
2168 0 : DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2169 0 : errno = EINVAL;
2170 0 : rc = -1;
2171 0 : goto exit;
2172 : }
2173 :
2174 0 : exit:
2175 0 : DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2176 : smb_fname->base_name, rc));
2177 :
2178 0 : if (rc != 0) {
2179 0 : ealen = -1;
2180 0 : if (errno == EINVAL) {
2181 0 : become_root();
2182 0 : (void)SMB_VFS_FREMOVEXATTR(fsp,
2183 : AFPINFO_EA_NETATALK);
2184 0 : unbecome_root();
2185 0 : errno = ENOENT;
2186 : }
2187 : }
2188 0 : return ealen;
2189 : }
2190 :
2191 0 : 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)
2196 : {
2197 0 : int rc = 0;
2198 0 : struct adouble *ad = NULL;
2199 0 : struct smb_filename *adp_smb_fname = NULL;
2200 0 : struct files_struct *ad_fsp = NULL;
2201 : NTSTATUS status;
2202 0 : struct vfs_open_how how = { .flags = in_flags, .mode = mode, };
2203 :
2204 0 : rc = adouble_path(talloc_tos(),
2205 : smb_base_fname,
2206 : &adp_smb_fname);
2207 0 : if (rc != 0) {
2208 0 : return NT_STATUS_NO_MEMORY;
2209 : }
2210 :
2211 0 : status = create_internal_fsp(dirfsp->conn,
2212 : adp_smb_fname,
2213 : &ad_fsp);
2214 0 : if (!NT_STATUS_IS_OK(status)) {
2215 0 : return status;
2216 : }
2217 :
2218 : #ifdef O_PATH
2219 0 : how.flags &= ~(O_PATH);
2220 : #endif
2221 0 : if (how.flags & (O_CREAT | O_TRUNC | O_WRONLY)) {
2222 : /* We always need read/write access for the metadata header too */
2223 0 : how.flags &= ~(O_WRONLY);
2224 0 : how.flags |= O_RDWR;
2225 : }
2226 :
2227 0 : status = fd_openat(dirfsp,
2228 : adp_smb_fname,
2229 : ad_fsp,
2230 : &how);
2231 0 : if (!NT_STATUS_IS_OK(status)) {
2232 0 : file_free(NULL, ad_fsp);
2233 0 : return status;
2234 : }
2235 :
2236 0 : if (how.flags & (O_CREAT | O_TRUNC)) {
2237 0 : ad = ad_init(talloc_tos(), ADOUBLE_RSRC);
2238 0 : if (ad == NULL) {
2239 0 : file_free(NULL, ad_fsp);
2240 0 : return NT_STATUS_NO_MEMORY;
2241 : }
2242 :
2243 0 : rc = ad_fset(ad_fsp->conn->vfs_handles, ad, ad_fsp);
2244 0 : if (rc != 0) {
2245 0 : file_free(NULL, ad_fsp);
2246 0 : return NT_STATUS_IO_DEVICE_ERROR;
2247 : }
2248 0 : TALLOC_FREE(ad);
2249 : }
2250 :
2251 0 : *_ad_fsp = ad_fsp;
2252 0 : return NT_STATUS_OK;
2253 : }
2254 :
2255 0 : 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)
2261 : {
2262 0 : *_ad_fsp = NULL;
2263 :
2264 0 : SMB_ASSERT(base_fsp != NULL);
2265 0 : SMB_ASSERT(!fsp_is_alternate_stream(base_fsp));
2266 :
2267 0 : switch (type) {
2268 0 : case ADOUBLE_META:
2269 0 : return NT_STATUS_INTERNAL_ERROR;
2270 0 : case ADOUBLE_RSRC:
2271 0 : return adouble_open_rsrc_fsp(dirfsp,
2272 0 : base_fsp->fsp_name,
2273 : flags,
2274 : mode,
2275 : _ad_fsp);
2276 : }
2277 :
2278 0 : return NT_STATUS_INTERNAL_ERROR;
2279 : }
2280 :
2281 : /*
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.
2285 : */
2286 0 : 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)
2292 : {
2293 : NTSTATUS status;
2294 :
2295 0 : DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2296 : ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2297 :
2298 0 : if (ad->ad_type == ADOUBLE_META) {
2299 0 : return 0;
2300 : }
2301 :
2302 0 : if (fsp != NULL) {
2303 0 : ad->ad_fsp = fsp;
2304 0 : ad->ad_opened = false;
2305 0 : return 0;
2306 : }
2307 :
2308 0 : status = adouble_open_rsrc_fsp(handle->conn->cwd_fsp,
2309 : smb_fname,
2310 : flags,
2311 : mode,
2312 0 : &ad->ad_fsp);
2313 0 : if (!NT_STATUS_IS_OK(status)) {
2314 0 : errno = map_errno_from_nt_status(status);
2315 0 : return -1;
2316 : }
2317 0 : ad->ad_opened = true;
2318 :
2319 0 : DBG_DEBUG("Path [%s] type [%s]\n",
2320 : smb_fname->base_name,
2321 : ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2322 :
2323 0 : return 0;
2324 : }
2325 :
2326 0 : static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2327 : struct adouble *ad,
2328 : const struct smb_filename *smb_fname)
2329 : {
2330 : size_t to_read;
2331 : ssize_t len;
2332 : int ret;
2333 : bool ok;
2334 :
2335 0 : ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2336 0 : if (ret != 0) {
2337 0 : DBG_ERR("fstat [%s] failed: %s\n",
2338 : fsp_str_dbg(ad->ad_fsp), strerror(errno));
2339 0 : return -1;
2340 : }
2341 :
2342 0 : to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2343 0 : if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2344 0 : to_read = AD_XATTR_MAX_HDR_SIZE;
2345 : }
2346 :
2347 0 : len = SMB_VFS_NEXT_PREAD(handle,
2348 : ad->ad_fsp,
2349 : ad->ad_data,
2350 : to_read,
2351 : 0);
2352 0 : if (len != to_read) {
2353 0 : DBG_NOTICE("%s %s: bad size: %zd\n",
2354 : smb_fname->base_name, strerror(errno), len);
2355 0 : return -1;
2356 : }
2357 :
2358 : /* Now parse entries */
2359 0 : ok = ad_unpack(ad,
2360 : ADEID_NUM_DOT_UND,
2361 0 : ad->ad_fsp->fsp_name->st.st_ex_size);
2362 0 : if (!ok) {
2363 0 : DBG_ERR("invalid AppleDouble resource %s\n",
2364 : smb_fname->base_name);
2365 0 : errno = EINVAL;
2366 0 : return -1;
2367 : }
2368 :
2369 0 : if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2370 0 : || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2371 0 : || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2372 : {
2373 0 : DBG_ERR("invalid AppleDouble resource %s\n",
2374 : smb_fname->base_name);
2375 0 : errno = EINVAL;
2376 0 : return -1;
2377 : }
2378 :
2379 0 : return len;
2380 : }
2381 :
2382 : /**
2383 : * Read and parse resource fork, either ._ AppleDouble file or xattr
2384 : **/
2385 0 : static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2386 : struct adouble *ad,
2387 : const struct smb_filename *smb_fname)
2388 : {
2389 0 : return ad_read_rsrc_adouble(handle, ad, smb_fname);
2390 : }
2391 :
2392 : /**
2393 : * Read and unpack an AppleDouble metadata xattr or resource
2394 : **/
2395 0 : static ssize_t ad_read(vfs_handle_struct *handle,
2396 : struct adouble *ad,
2397 : const struct smb_filename *smb_fname)
2398 : {
2399 0 : switch (ad->ad_type) {
2400 0 : case ADOUBLE_META:
2401 0 : return ad_read_meta(handle, ad, smb_fname);
2402 0 : case ADOUBLE_RSRC:
2403 0 : return ad_read_rsrc(handle, ad, smb_fname);
2404 0 : default:
2405 0 : return -1;
2406 : }
2407 : }
2408 :
2409 0 : static int adouble_destructor(struct adouble *ad)
2410 : {
2411 : NTSTATUS status;
2412 :
2413 0 : if (!ad->ad_opened) {
2414 0 : return 0;
2415 : }
2416 :
2417 0 : SMB_ASSERT(ad->ad_fsp != NULL);
2418 :
2419 0 : status = fd_close(ad->ad_fsp);
2420 0 : if (!NT_STATUS_IS_OK(status)) {
2421 0 : DBG_ERR("Closing [%s] failed: %s\n",
2422 : fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2423 : }
2424 0 : file_free(NULL, ad->ad_fsp);
2425 0 : ad->ad_fsp = NULL;
2426 0 : ad->ad_opened = false;
2427 :
2428 0 : return 0;
2429 : }
2430 :
2431 : /**
2432 : * Allocate a struct adouble without initialiing it
2433 : *
2434 : * The struct is either hang of the fsp extension context or if fsp is
2435 : * NULL from ctx.
2436 : *
2437 : * @param[in] ctx talloc context
2438 : * @param[in] handle vfs handle
2439 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2440 : *
2441 : * @return adouble handle
2442 : **/
2443 0 : static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2444 : adouble_type_t type)
2445 : {
2446 0 : int rc = 0;
2447 0 : size_t adsize = 0;
2448 : struct adouble *ad;
2449 :
2450 0 : switch (type) {
2451 0 : case ADOUBLE_META:
2452 0 : adsize = AD_DATASZ_XATTR;
2453 0 : break;
2454 0 : case ADOUBLE_RSRC:
2455 : /*
2456 : * AppleDouble ._ file case, optimize for fewer (but larger)
2457 : * IOs. Two cases:
2458 : *
2459 : * - without xattrs size of the header is exactly
2460 : * AD_DATASZ_DOT_UND (82) bytes
2461 : *
2462 : * - with embedded xattrs it can be larger, up to
2463 : * AD_XATTR_MAX_HDR_SIZE
2464 : *
2465 : * Larger headers are not supported, but this is a reasonable
2466 : * limit that is also employed by the macOS client.
2467 : *
2468 : * We used the largest possible size to be able to read the full
2469 : * header with one IO.
2470 : */
2471 0 : adsize = AD_XATTR_MAX_HDR_SIZE;
2472 0 : break;
2473 0 : default:
2474 0 : return NULL;
2475 : }
2476 :
2477 0 : ad = talloc_zero(ctx, struct adouble);
2478 0 : if (ad == NULL) {
2479 0 : rc = -1;
2480 0 : goto exit;
2481 : }
2482 :
2483 0 : if (adsize) {
2484 0 : ad->ad_data = talloc_zero_array(ad, char, adsize);
2485 0 : if (ad->ad_data == NULL) {
2486 0 : rc = -1;
2487 0 : goto exit;
2488 : }
2489 : }
2490 :
2491 0 : ad->ad_type = type;
2492 0 : ad->ad_magic = AD_MAGIC;
2493 0 : ad->ad_version = AD_VERSION;
2494 :
2495 0 : talloc_set_destructor(ad, adouble_destructor);
2496 :
2497 0 : exit:
2498 0 : if (rc != 0) {
2499 0 : TALLOC_FREE(ad);
2500 : }
2501 0 : return ad;
2502 : }
2503 :
2504 : /**
2505 : * Allocate and initialize a new struct adouble
2506 : *
2507 : * @param[in] ctx talloc context
2508 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2509 : *
2510 : * @return adouble handle, initialized
2511 : **/
2512 0 : struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2513 : {
2514 0 : int rc = 0;
2515 : const struct ad_entry_order *eid;
2516 0 : struct adouble *ad = NULL;
2517 0 : time_t t = time(NULL);
2518 :
2519 0 : switch (type) {
2520 0 : case ADOUBLE_META:
2521 0 : eid = entry_order_meta_xattr;
2522 0 : break;
2523 0 : case ADOUBLE_RSRC:
2524 0 : eid = entry_order_dot_und;
2525 0 : break;
2526 0 : default:
2527 0 : return NULL;
2528 : }
2529 :
2530 0 : ad = ad_alloc(ctx, type);
2531 0 : if (ad == NULL) {
2532 0 : return NULL;
2533 : }
2534 :
2535 0 : while (eid->id) {
2536 0 : ad->ad_eid[eid->id].ade_off = eid->offset;
2537 0 : ad->ad_eid[eid->id].ade_len = eid->len;
2538 0 : eid++;
2539 : }
2540 :
2541 : /* put something sane in the date fields */
2542 0 : ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2543 0 : ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2544 0 : ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2545 0 : ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2546 :
2547 0 : if (rc != 0) {
2548 0 : TALLOC_FREE(ad);
2549 : }
2550 0 : return ad;
2551 : }
2552 :
2553 0 : 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)
2558 : {
2559 0 : int rc = 0;
2560 : ssize_t len;
2561 0 : struct adouble *ad = NULL;
2562 : int mode;
2563 :
2564 0 : if (fsp != NULL) {
2565 0 : if (fsp_is_alternate_stream(fsp)) {
2566 0 : smb_fname = fsp->base_fsp->fsp_name;
2567 : } else {
2568 0 : smb_fname = fsp->fsp_name;
2569 : }
2570 : }
2571 :
2572 0 : DEBUG(10, ("ad_get(%s) called for %s\n",
2573 : type == ADOUBLE_META ? "meta" : "rsrc",
2574 : smb_fname != NULL ? smb_fname->base_name : "???"));
2575 :
2576 0 : ad = ad_alloc(ctx, type);
2577 0 : if (ad == NULL) {
2578 0 : rc = -1;
2579 0 : goto exit;
2580 : }
2581 :
2582 : /* Try rw first so we can use the fd in ad_convert() */
2583 0 : mode = O_RDWR;
2584 :
2585 0 : rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2586 0 : if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2587 0 : mode = O_RDONLY;
2588 0 : rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2589 : }
2590 0 : if (rc == -1) {
2591 0 : DBG_DEBUG("ad_open [%s] error [%s]\n",
2592 : smb_fname->base_name, strerror(errno));
2593 0 : goto exit;
2594 :
2595 : }
2596 :
2597 0 : len = ad_read(handle, ad, smb_fname);
2598 0 : if (len == -1) {
2599 0 : DEBUG(10, ("error reading AppleDouble for %s\n",
2600 : smb_fname->base_name));
2601 0 : rc = -1;
2602 0 : goto exit;
2603 : }
2604 :
2605 0 : exit:
2606 0 : DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2607 : type == ADOUBLE_META ? "meta" : "rsrc",
2608 : smb_fname->base_name, rc));
2609 :
2610 0 : if (rc != 0) {
2611 0 : TALLOC_FREE(ad);
2612 : }
2613 0 : return ad;
2614 : }
2615 :
2616 : /**
2617 : * Return AppleDouble data for a file
2618 : *
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
2623 : *
2624 : * @return talloced struct adouble or NULL on error
2625 : **/
2626 0 : struct adouble *ad_get(TALLOC_CTX *ctx,
2627 : vfs_handle_struct *handle,
2628 : const struct smb_filename *smb_fname,
2629 : adouble_type_t type)
2630 : {
2631 0 : return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2632 : }
2633 :
2634 : /**
2635 : * Return AppleDouble data for a file
2636 : *
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
2641 : *
2642 : * @return talloced struct adouble or NULL on error
2643 : **/
2644 0 : struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2645 : files_struct *fsp, adouble_type_t type)
2646 : {
2647 0 : return ad_get_internal(ctx, handle, fsp, NULL, type);
2648 : }
2649 :
2650 : /**
2651 : * Set AppleDouble metadata on a file or directory
2652 : *
2653 : * @param[in] ad adouble handle
2654 : * @param[in] fsp file handle
2655 : *
2656 : * @return status code, 0 means success
2657 : **/
2658 0 : int ad_fset(struct vfs_handle_struct *handle,
2659 : struct adouble *ad,
2660 : files_struct *fsp)
2661 : {
2662 0 : int rc = -1;
2663 : ssize_t len;
2664 : bool ok;
2665 :
2666 0 : DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2667 :
2668 0 : ok = ad_pack(handle, ad, fsp);
2669 0 : if (!ok) {
2670 0 : return -1;
2671 : }
2672 :
2673 0 : switch (ad->ad_type) {
2674 0 : case ADOUBLE_META:
2675 0 : 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 0 : break;
2681 0 : case ADOUBLE_RSRC:
2682 0 : len = SMB_VFS_NEXT_PWRITE(handle,
2683 : fsp,
2684 : ad->ad_data,
2685 : ad_getentryoff(ad, ADEID_RFORK),
2686 : 0);
2687 0 : if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2688 0 : DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2689 0 : return -1;
2690 : }
2691 0 : rc = 0;
2692 0 : break;
2693 :
2694 0 : default:
2695 0 : return -1;
2696 : }
2697 :
2698 0 : DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2699 :
2700 0 : return rc;
2701 : }
2702 :
2703 0 : bool is_adouble_file(const char *path)
2704 : {
2705 0 : const char *p = NULL;
2706 : int match;
2707 :
2708 0 : p = strrchr(path, '/');
2709 0 : if (p == NULL) {
2710 0 : p = path;
2711 : } else {
2712 0 : p++;
2713 : }
2714 :
2715 0 : match = strncmp(p,
2716 : ADOUBLE_NAME_PREFIX,
2717 : strlen(ADOUBLE_NAME_PREFIX));
2718 0 : if (match != 0) {
2719 0 : return false;
2720 : }
2721 0 : return true;
2722 : }
2723 :
2724 : /**
2725 : * Prepend "._" to a basename
2726 : * Return a new struct smb_filename with stream_name == NULL.
2727 : **/
2728 0 : int adouble_path(TALLOC_CTX *ctx,
2729 : const struct smb_filename *smb_fname_in,
2730 : struct smb_filename **pp_smb_fname_out)
2731 : {
2732 : char *parent;
2733 : const char *base;
2734 0 : struct smb_filename *smb_fname = NULL;
2735 :
2736 0 : smb_fname = cp_smb_filename_nostream(ctx, smb_fname_in);
2737 0 : if (smb_fname == NULL) {
2738 0 : return -1;
2739 : }
2740 :
2741 : /* We're replacing base_name. */
2742 0 : TALLOC_FREE(smb_fname->base_name);
2743 :
2744 0 : SET_STAT_INVALID(smb_fname->st);
2745 :
2746 0 : if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2747 : &parent, &base)) {
2748 0 : TALLOC_FREE(smb_fname);
2749 0 : return -1;
2750 : }
2751 :
2752 0 : if (ISDOT(parent)) {
2753 0 : smb_fname->base_name = talloc_asprintf(smb_fname,
2754 : "._%s", base);
2755 : } else {
2756 0 : smb_fname->base_name = talloc_asprintf(smb_fname,
2757 : "%s/._%s", parent, base);
2758 : }
2759 0 : if (smb_fname->base_name == NULL) {
2760 0 : TALLOC_FREE(smb_fname);
2761 0 : return -1;
2762 : }
2763 :
2764 0 : *pp_smb_fname_out = smb_fname;
2765 :
2766 0 : return 0;
2767 : }
2768 :
2769 : /**
2770 : * Allocate and initialize an AfpInfo struct
2771 : **/
2772 0 : AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2773 : {
2774 0 : AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2775 0 : if (ai == NULL) {
2776 0 : return NULL;
2777 : }
2778 0 : ai->afpi_Signature = AFP_Signature;
2779 0 : ai->afpi_Version = AFP_Version;
2780 0 : ai->afpi_BackupTime = AD_DATE_START;
2781 0 : return ai;
2782 : }
2783 :
2784 : /**
2785 : * Pack an AfpInfo struct into a buffer
2786 : *
2787 : * Buffer size must be at least AFP_INFO_SIZE
2788 : * Returns size of packed buffer
2789 : **/
2790 0 : ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2791 : {
2792 0 : memset(buf, 0, AFP_INFO_SIZE);
2793 :
2794 0 : RSIVAL(buf, 0, ai->afpi_Signature);
2795 0 : RSIVAL(buf, 4, ai->afpi_Version);
2796 0 : RSIVAL(buf, 12, ai->afpi_BackupTime);
2797 0 : memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2798 :
2799 0 : return AFP_INFO_SIZE;
2800 : }
2801 :
2802 : /**
2803 : * Unpack a buffer into a AfpInfo structure
2804 : *
2805 : * Buffer size must be at least AFP_INFO_SIZE
2806 : * Returns allocated AfpInfo struct
2807 : **/
2808 0 : AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2809 : {
2810 0 : AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2811 0 : if (ai == NULL) {
2812 0 : return NULL;
2813 : }
2814 :
2815 0 : ai->afpi_Signature = RIVAL(data, 0);
2816 0 : ai->afpi_Version = RIVAL(data, 4);
2817 0 : ai->afpi_BackupTime = RIVAL(data, 12);
2818 0 : memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2819 : sizeof(ai->afpi_FinderInfo));
2820 :
2821 0 : if (ai->afpi_Signature != AFP_Signature
2822 0 : || ai->afpi_Version != AFP_Version) {
2823 0 : DEBUG(1, ("Bad AfpInfo signature or version\n"));
2824 0 : TALLOC_FREE(ai);
2825 : }
2826 :
2827 0 : return ai;
2828 : }
|