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 : * preceded 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 9397 : size_t ad_getentrylen(const struct adouble *ad, int eid)
253 : {
254 9397 : return ad->ad_eid[eid].ade_len;
255 : }
256 :
257 17327 : size_t ad_getentryoff(const struct adouble *ad, int eid)
258 : {
259 17327 : return ad->ad_eid[eid].ade_off;
260 : }
261 :
262 196 : size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
263 : {
264 196 : return ad->ad_eid[eid].ade_len = len;
265 : }
266 :
267 34 : size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
268 : {
269 34 : 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 16265 : static bool ad_entry_check_size(uint32_t eid,
281 : size_t bufsize,
282 : uint32_t off,
283 : uint32_t got_len)
284 : {
285 15 : struct {
286 : off_t expected_len;
287 : bool fixed_size;
288 : bool minimum_size;
289 16265 : } 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 16265 : if (eid >= ADEID_MAX) {
312 0 : return false;
313 : }
314 16265 : if (got_len == 0) {
315 : /* Entry present, but empty, allow */
316 7060 : return true;
317 : }
318 9201 : 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 9201 : if (ad_checks[eid].expected_len == -1) {
326 : /* Unused or no limit */
327 1618 : return true;
328 : }
329 7581 : if (ad_checks[eid].fixed_size) {
330 3545 : if (ad_checks[eid].expected_len != got_len) {
331 : /* Wrong size for fixed size entry. */
332 0 : return false;
333 : }
334 : } else {
335 4036 : if (ad_checks[eid].minimum_size) {
336 4035 : 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 1 : if (got_len > ad_checks[eid].expected_len) {
345 : /* Too big for variable sized entry. */
346 0 : return false;
347 : }
348 : }
349 : }
350 7579 : if (off + got_len < off) {
351 : /* wrap around */
352 0 : return false;
353 : }
354 7579 : if (off + got_len > bufsize) {
355 : /* overflow */
356 2 : return false;
357 : }
358 7572 : 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 1973 : char *ad_get_entry(const struct adouble *ad, int eid)
367 : {
368 1973 : size_t bufsize = talloc_get_size(ad->ad_data);
369 1973 : off_t off = ad_getentryoff(ad, eid);
370 1973 : size_t len = ad_getentrylen(ad, eid);
371 3 : bool valid;
372 :
373 1973 : valid = ad_entry_check_size(eid, bufsize, off, len);
374 1973 : if (!valid) {
375 0 : return NULL;
376 : }
377 :
378 1973 : if (off == 0 || len == 0) {
379 576 : return NULL;
380 : }
381 :
382 1395 : return ad->ad_data + off;
383 : }
384 :
385 : /**
386 : * Get a date
387 : **/
388 364 : int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
389 : {
390 364 : bool xlate = (dateoff & AD_DATE_UNIX);
391 364 : char *p = NULL;
392 :
393 364 : dateoff &= AD_DATE_MASK;
394 364 : p = ad_get_entry(ad, ADEID_FILEDATESI);
395 364 : if (p == NULL) {
396 0 : return -1;
397 : }
398 :
399 364 : if (dateoff > AD_DATE_ACCESS) {
400 0 : return -1;
401 : }
402 :
403 364 : memcpy(date, p + dateoff, sizeof(uint32_t));
404 :
405 364 : if (xlate) {
406 364 : *date = AD_DATE_TO_UNIX(*date);
407 : }
408 364 : return 0;
409 : }
410 :
411 : /**
412 : * Set a date
413 : **/
414 1064 : int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
415 : {
416 1064 : bool xlate = (dateoff & AD_DATE_UNIX);
417 1064 : char *p = NULL;
418 :
419 1064 : p = ad_get_entry(ad, ADEID_FILEDATESI);
420 1064 : if (p == NULL) {
421 576 : return -1;
422 : }
423 :
424 488 : dateoff &= AD_DATE_MASK;
425 488 : if (xlate) {
426 366 : date = AD_DATE_FROM_UNIX(date);
427 : }
428 :
429 488 : if (dateoff > AD_DATE_ACCESS) {
430 0 : return -1;
431 : }
432 :
433 488 : memcpy(p + dateoff, &date, sizeof(date));
434 :
435 488 : return 0;
436 : }
437 :
438 :
439 : /**
440 : * Map on-disk AppleDouble id to enumerated id
441 : **/
442 14292 : static uint32_t get_eid(uint32_t eid)
443 : {
444 14292 : if (eid <= 15) {
445 9408 : return eid;
446 : }
447 :
448 4872 : switch (eid) {
449 1218 : case AD_DEV:
450 1218 : return ADEID_PRIVDEV;
451 1218 : case AD_INO:
452 1218 : return ADEID_PRIVINO;
453 1218 : case AD_SYN:
454 1218 : return ADEID_PRIVSYN;
455 1218 : case AD_ID:
456 1218 : 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 6 : static bool ad_pack_move_reso(struct vfs_handle_struct *handle,
476 : struct adouble *ad,
477 : files_struct *fsp)
478 : {
479 0 : size_t reso_len;
480 0 : size_t reso_off;
481 0 : size_t n;
482 0 : bool ok;
483 :
484 6 : reso_len = ad_getentrylen(ad, ADEID_RFORK);
485 6 : reso_off = ad_getentryoff(ad, ADEID_RFORK);
486 :
487 6 : if (reso_len == 0) {
488 0 : return true;
489 : }
490 :
491 6 : 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 6 : n = SMB_VFS_NEXT_PWRITE(handle,
517 : fsp,
518 : ad->ad_rsrc_data,
519 : reso_len,
520 : reso_off);
521 6 : 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 6 : ok = true;
529 6 : out:
530 6 : return ok;
531 : }
532 :
533 492 : static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
534 : struct adouble *ad,
535 : files_struct *fsp)
536 : {
537 492 : struct ad_xattr_header *h = &ad->adx_header;
538 0 : size_t oldsize;
539 0 : uint32_t off;
540 0 : uint32_t data_off;
541 0 : uint16_t i;
542 0 : bool ok;
543 :
544 492 : if (ad->adx_entries == NULL) {
545 : /* No xattrs, nothing to pack */
546 486 : return true;
547 : }
548 :
549 6 : if (fsp == NULL) {
550 0 : DBG_ERR("fsp unexpectedly NULL\n");
551 0 : return false;
552 : }
553 :
554 6 : oldsize = talloc_get_size(ad->ad_data);
555 6 : 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 6 : data_off = ad_getentryoff(ad, ADEID_FINDERI);
574 6 : data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
575 : /* 2 bytes padding */
576 6 : data_off += 2;
577 :
578 12 : for (i = 0; i < h->adx_num_attrs; i++) {
579 6 : struct ad_xattr_entry *e = &ad->adx_entries[i];
580 :
581 : /* Align on 4 byte boundary */
582 6 : data_off = (data_off + 3) & ~3;
583 :
584 6 : data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
585 6 : if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
586 0 : return false;
587 : }
588 : }
589 :
590 6 : off = ad_getentryoff(ad, ADEID_FINDERI);
591 6 : off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
592 : /* 2 bytes padding */
593 6 : off += 2;
594 :
595 12 : for (i = 0; i < h->adx_num_attrs; i++) {
596 6 : struct ad_xattr_entry *e = &ad->adx_entries[i];
597 :
598 : /* Align on 4 byte boundary */
599 6 : off = (off + 3) & ~3;
600 :
601 6 : e->adx_offset = data_off;
602 6 : data_off += e->adx_length;
603 :
604 6 : 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 6 : if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
614 0 : return false;
615 : }
616 6 : RSIVAL(ad->ad_data, off, e->adx_offset);
617 6 : off += 4;
618 :
619 6 : if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
620 0 : return false;
621 : }
622 6 : RSIVAL(ad->ad_data, off, e->adx_length);
623 6 : off += 4;
624 :
625 6 : if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
626 0 : return false;
627 : }
628 6 : RSSVAL(ad->ad_data, off, e->adx_flags);
629 6 : off += 2;
630 :
631 6 : if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
632 0 : return false;
633 : }
634 6 : SCVAL(ad->ad_data, off, e->adx_namelen);
635 6 : off += 1;
636 :
637 6 : if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
638 0 : return false;
639 : }
640 6 : memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
641 6 : off += e->adx_namelen;
642 : }
643 :
644 6 : h->adx_data_start = off;
645 6 : h->adx_data_length = talloc_get_size(ad->adx_data);
646 6 : h->adx_total_size = h->adx_data_start + h->adx_data_length;
647 :
648 6 : 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 6 : memcpy(ad->ad_data + h->adx_data_start,
659 6 : ad->adx_data,
660 6 : h->adx_data_length);
661 :
662 6 : ad_setentrylen(ad,
663 : ADEID_FINDERI,
664 6 : h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));
665 :
666 6 : ad_setentryoff(ad,
667 : ADEID_RFORK,
668 6 : ad_getentryoff(ad, ADEID_FINDERI) +
669 6 : ad_getentrylen(ad, ADEID_FINDERI));
670 :
671 6 : memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
672 :
673 : /*
674 : * Rewind, then update the header fields.
675 : */
676 :
677 6 : off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
678 : /* 2 bytes padding */
679 6 : off += 2;
680 :
681 6 : RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
682 6 : off += 4;
683 6 : RSIVAL(ad->ad_data, off, 0);
684 6 : off += 4;
685 6 : RSIVAL(ad->ad_data, off, h->adx_total_size);
686 6 : off += 4;
687 6 : RSIVAL(ad->ad_data, off, h->adx_data_start);
688 6 : off += 4;
689 6 : RSIVAL(ad->ad_data, off, h->adx_data_length);
690 6 : off += 4;
691 :
692 : /* adx_reserved and adx_flags */
693 6 : memset(ad->ad_data + off, 0, 3 * 4 + 2);
694 6 : off += 3 * 4 + 2;
695 :
696 6 : RSSVAL(ad->ad_data, off, h->adx_num_attrs);
697 6 : off += 2;
698 :
699 6 : ok = ad_pack_move_reso(handle, ad, fsp);
700 6 : if (!ok) {
701 0 : DBG_ERR("Moving resourcefork of [%s] failed\n",
702 : fsp_str_dbg(fsp));
703 0 : return false;
704 : }
705 :
706 6 : return true;
707 : }
708 :
709 : /**
710 : * Pack AppleDouble structure into data buffer
711 : **/
712 492 : static bool ad_pack(struct vfs_handle_struct *handle,
713 : struct adouble *ad,
714 : files_struct *fsp)
715 : {
716 0 : uint32_t eid;
717 0 : uint16_t nent;
718 0 : uint32_t bufsize;
719 492 : uint32_t offset = 0;
720 0 : bool ok;
721 :
722 492 : bufsize = talloc_get_size(ad->ad_data);
723 492 : if (bufsize < AD_DATASZ_DOT_UND) {
724 0 : DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
725 0 : return false;
726 : }
727 :
728 492 : if (offset + ADEDLEN_MAGIC < offset ||
729 492 : offset + ADEDLEN_MAGIC >= bufsize) {
730 0 : return false;
731 : }
732 492 : RSIVAL(ad->ad_data, offset, ad->ad_magic);
733 492 : offset += ADEDLEN_MAGIC;
734 :
735 492 : if (offset + ADEDLEN_VERSION < offset ||
736 492 : offset + ADEDLEN_VERSION >= bufsize) {
737 0 : return false;
738 : }
739 492 : RSIVAL(ad->ad_data, offset, ad->ad_version);
740 492 : offset += ADEDLEN_VERSION;
741 :
742 492 : if (offset + ADEDLEN_FILLER < offset ||
743 492 : offset + ADEDLEN_FILLER >= bufsize) {
744 0 : return false;
745 : }
746 492 : if (ad->ad_type == ADOUBLE_RSRC) {
747 356 : memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
748 : }
749 492 : offset += ADEDLEN_FILLER;
750 :
751 492 : if (offset + ADEDLEN_NENTRIES < offset ||
752 492 : offset + ADEDLEN_NENTRIES >= bufsize) {
753 0 : return false;
754 : }
755 492 : offset += ADEDLEN_NENTRIES;
756 :
757 492 : ok = ad_pack_xattrs(handle, ad, fsp);
758 492 : if (!ok) {
759 0 : return false;
760 : }
761 :
762 10332 : for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
763 9840 : 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 8040 : continue;
769 : }
770 :
771 1800 : if (offset + AD_ENTRY_LEN_EID < offset ||
772 1800 : offset + AD_ENTRY_LEN_EID >= bufsize) {
773 0 : return false;
774 : }
775 1800 : RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
776 1800 : offset += AD_ENTRY_LEN_EID;
777 :
778 1800 : if (offset + AD_ENTRY_LEN_OFF < offset ||
779 1800 : offset + AD_ENTRY_LEN_OFF >= bufsize) {
780 0 : return false;
781 : }
782 1800 : RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
783 1800 : offset += AD_ENTRY_LEN_OFF;
784 :
785 1800 : if (offset + AD_ENTRY_LEN_LEN < offset ||
786 1800 : offset + AD_ENTRY_LEN_LEN >= bufsize) {
787 0 : return false;
788 : }
789 1800 : RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
790 1800 : offset += AD_ENTRY_LEN_LEN;
791 :
792 1800 : nent++;
793 : }
794 :
795 492 : if (ADEDOFF_NENTRIES + 2 >= bufsize) {
796 0 : return false;
797 : }
798 492 : RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
799 :
800 492 : return true;
801 : }
802 :
803 2268 : static bool ad_unpack_xattrs(struct adouble *ad)
804 : {
805 2268 : struct ad_xattr_header *h = &ad->adx_header;
806 2268 : size_t bufsize = talloc_get_size(ad->ad_data);
807 2268 : const char *p = ad->ad_data;
808 0 : uint32_t hoff;
809 0 : uint32_t i;
810 :
811 2268 : if (ad->ad_type != ADOUBLE_RSRC) {
812 0 : return false;
813 : }
814 :
815 2268 : if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
816 2234 : 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 34 : if (bufsize != AD_XATTR_MAX_HDR_SIZE) {
824 0 : return false;
825 : }
826 :
827 : /* 2 bytes padding */
828 34 : hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
829 :
830 34 : h->adx_magic = RIVAL(p, hoff + 0);
831 34 : h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
832 34 : h->adx_total_size = RIVAL(p, hoff + 8);
833 34 : h->adx_data_start = RIVAL(p, hoff + 12);
834 34 : h->adx_data_length = RIVAL(p, hoff + 16);
835 34 : h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
836 34 : h->adx_num_attrs = RSVAL(p, hoff + 34);
837 :
838 34 : 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 34 : 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 34 : 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 34 : 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 34 : 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 34 : if ((h->adx_data_start + h->adx_data_length) >
862 34 : 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 34 : 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 34 : if (h->adx_num_attrs == 0) {
874 0 : return true;
875 : }
876 :
877 34 : ad->adx_entries = talloc_zero_array(
878 : ad, struct ad_xattr_entry, h->adx_num_attrs);
879 34 : if (ad->adx_entries == NULL) {
880 0 : return false;
881 : }
882 :
883 34 : hoff += AD_XATTR_HDR_SIZE;
884 :
885 86 : for (i = 0; i < h->adx_num_attrs; i++) {
886 52 : struct ad_xattr_entry *e = &ad->adx_entries[i];
887 :
888 52 : hoff = (hoff + 3) & ~3;
889 :
890 52 : e->adx_offset = RIVAL(p, hoff + 0);
891 52 : e->adx_length = RIVAL(p, hoff + 4);
892 52 : e->adx_flags = RSVAL(p, hoff + 8);
893 52 : e->adx_namelen = *(p + hoff + 10);
894 :
895 52 : 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 52 : 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 52 : if ((e->adx_offset + e->adx_length) >
908 52 : 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 52 : if (e->adx_namelen == 0) {
916 0 : DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
917 : e->adx_namelen);
918 0 : return false;
919 : }
920 52 : 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 52 : if ((hoff + 11 + e->adx_namelen) >
926 52 : 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 104 : e->adx_name = talloc_strndup(ad->adx_entries,
934 52 : p + hoff + 11,
935 52 : e->adx_namelen);
936 52 : if (e->adx_name == NULL) {
937 0 : return false;
938 : }
939 :
940 52 : DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
941 : e->adx_name, e->adx_offset, e->adx_length);
942 52 : dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
943 52 : e->adx_length);
944 :
945 52 : hoff += 11 + e->adx_namelen;
946 : }
947 :
948 34 : return true;
949 : }
950 :
951 : /**
952 : * Unpack an AppleDouble blob into a struct adoble
953 : **/
954 3493 : static bool ad_unpack(struct adouble *ad, const size_t nentries,
955 : size_t filesize)
956 : {
957 3493 : size_t bufsize = talloc_get_size(ad->ad_data);
958 7 : size_t adentries, i;
959 7 : uint32_t eid, len, off;
960 7 : 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 3493 : if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
970 0 : DBG_NOTICE("Bad size\n");
971 0 : return false;
972 : }
973 :
974 3493 : ad->ad_magic = RIVAL(ad->ad_data, 0);
975 3493 : ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
976 3493 : if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
977 0 : DBG_NOTICE("Wrong magic or version\n");
978 0 : return false;
979 : }
980 :
981 3493 : memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
982 :
983 3493 : adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
984 3493 : if (adentries != nentries) {
985 0 : DBG_NOTICE("Invalid number of entries: %zu\n", adentries);
986 0 : return false;
987 : }
988 :
989 : /* now, read in the entry bits */
990 17781 : for (i = 0; i < adentries; i++) {
991 14292 : eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
992 14292 : eid = get_eid(eid);
993 14292 : off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
994 14292 : len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
995 :
996 14292 : if (!eid || eid >= ADEID_MAX) {
997 0 : DBG_NOTICE("Bogus eid %d\n", eid);
998 0 : return false;
999 : }
1000 :
1001 : /*
1002 : * All entries other than the resource fork are
1003 : * expected to be read into the ad_data buffer, so
1004 : * ensure the specified offset is within that bound
1005 : */
1006 14292 : if ((off > bufsize) && (eid != ADEID_RFORK)) {
1007 0 : DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1008 : ", len: %" PRIu32 "\n",
1009 : eid,
1010 : off,
1011 : len);
1012 0 : return false;
1013 : }
1014 :
1015 14292 : ok = ad_entry_check_size(eid, bufsize, off, len);
1016 14292 : if (!ok) {
1017 4 : DBG_NOTICE("Bogus eid [%" PRIu32 "] bufsize [%zu] "
1018 : "off [%" PRIu32 "] len [%" PRIu32 "]\n",
1019 : eid,
1020 : bufsize,
1021 : off,
1022 : len);
1023 4 : return false;
1024 : }
1025 :
1026 : /*
1027 : * That would be obviously broken
1028 : */
1029 14288 : if (off > filesize) {
1030 0 : DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1031 : ", len: %" PRIu32 "\n",
1032 : eid,
1033 : off,
1034 : len);
1035 0 : return false;
1036 : }
1037 :
1038 : /*
1039 : * Check for any entry that has its end beyond the
1040 : * filesize.
1041 : */
1042 14288 : if (off + len < off) {
1043 0 : DBG_NOTICE("offset wrap in eid %d: off: %" PRIu32
1044 : ", len: %" PRIu32 "\n",
1045 : eid,
1046 : off,
1047 : len);
1048 0 : return false;
1049 :
1050 : }
1051 14288 : if (off + len > filesize) {
1052 : /*
1053 : * If this is the resource fork entry, we fix
1054 : * up the length, for any other entry we bail
1055 : * out.
1056 : */
1057 0 : if (eid != ADEID_RFORK) {
1058 0 : DBG_NOTICE("Bogus eid %d: off: %" PRIu32
1059 : ", len: %" PRIu32 "\n",
1060 : eid,
1061 : off,
1062 : len);
1063 0 : return false;
1064 : }
1065 :
1066 : /*
1067 : * Fixup the resource fork entry by limiting
1068 : * the size to entryoffset - filesize.
1069 : */
1070 0 : len = filesize - off;
1071 0 : DBG_NOTICE("Limiting ADEID_RFORK: off: %" PRIu32
1072 : ", len: %" PRIu32 "\n",
1073 : off,
1074 : len);
1075 : }
1076 :
1077 14288 : ad->ad_eid[eid].ade_off = off;
1078 14288 : ad->ad_eid[eid].ade_len = len;
1079 : }
1080 :
1081 3489 : if (ad->ad_type == ADOUBLE_RSRC) {
1082 2268 : ok = ad_unpack_xattrs(ad);
1083 2268 : if (!ok) {
1084 0 : return false;
1085 : }
1086 : }
1087 :
1088 3486 : return true;
1089 : }
1090 :
1091 28 : static bool ad_convert_move_reso(vfs_handle_struct *handle,
1092 : struct adouble *ad,
1093 : const struct smb_filename *smb_fname)
1094 : {
1095 28 : char *buf = NULL;
1096 0 : size_t rforklen;
1097 0 : size_t rforkoff;
1098 0 : ssize_t n;
1099 0 : int ret;
1100 :
1101 28 : rforklen = ad_getentrylen(ad, ADEID_RFORK);
1102 28 : if (rforklen == 0) {
1103 0 : return true;
1104 : }
1105 :
1106 28 : buf = talloc_size(ad, rforklen);
1107 28 : if (buf == NULL) {
1108 : /*
1109 : * This allocates a buffer for reading the resource fork data in
1110 : * one big swoop. Resource forks won't be larger then, say, 64
1111 : * MB, I swear, so just doing the allocation with the talloc
1112 : * limit as safeguard seems safe.
1113 : */
1114 0 : DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1115 : rforklen);
1116 0 : return false;
1117 : }
1118 :
1119 28 : rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1120 :
1121 28 : n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1122 28 : if (n != rforklen) {
1123 0 : DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1124 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1125 0 : return false;
1126 : }
1127 :
1128 28 : rforkoff = ADEDOFF_RFORK_DOT_UND;
1129 :
1130 28 : n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1131 28 : if (n != rforklen) {
1132 0 : DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1133 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1134 0 : return false;
1135 : }
1136 :
1137 28 : ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1138 :
1139 28 : ret = ad_fset(handle, ad, ad->ad_fsp);
1140 28 : if (ret != 0) {
1141 0 : DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1142 0 : return false;
1143 : }
1144 :
1145 28 : return true;
1146 : }
1147 :
1148 516 : static bool ad_convert_xattr(vfs_handle_struct *handle,
1149 : struct adouble *ad,
1150 : const struct smb_filename *smb_fname,
1151 : const char *catia_mappings,
1152 : bool *converted_xattr)
1153 : {
1154 0 : static struct char_mappings **string_replace_cmaps = NULL;
1155 0 : uint16_t i;
1156 516 : int saved_errno = 0;
1157 0 : NTSTATUS status;
1158 0 : int rc;
1159 0 : bool ok;
1160 :
1161 516 : *converted_xattr = false;
1162 :
1163 516 : if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1164 488 : return true;
1165 : }
1166 :
1167 28 : if (string_replace_cmaps == NULL) {
1168 20 : const char **mappings = NULL;
1169 :
1170 20 : mappings = str_list_make_v3_const(
1171 : talloc_tos(), catia_mappings, NULL);
1172 20 : if (mappings == NULL) {
1173 0 : return false;
1174 : }
1175 40 : string_replace_cmaps = string_replace_init_map(
1176 20 : handle->conn->sconn, mappings);
1177 20 : TALLOC_FREE(mappings);
1178 : }
1179 :
1180 68 : for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1181 40 : struct ad_xattr_entry *e = &ad->adx_entries[i];
1182 40 : char *mapped_name = NULL;
1183 40 : char *tmp = NULL;
1184 40 : struct smb_filename *stream_name = NULL;
1185 40 : files_struct *fsp = NULL;
1186 0 : ssize_t nwritten;
1187 :
1188 40 : status = string_replace_allocate(handle->conn,
1189 40 : e->adx_name,
1190 : string_replace_cmaps,
1191 : talloc_tos(),
1192 : &mapped_name,
1193 : vfs_translate_to_windows);
1194 40 : if (!NT_STATUS_IS_OK(status) &&
1195 0 : !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1196 : {
1197 0 : DBG_ERR("string_replace_allocate failed\n");
1198 0 : ok = false;
1199 0 : goto fail;
1200 : }
1201 :
1202 40 : tmp = mapped_name;
1203 40 : mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1204 40 : TALLOC_FREE(tmp);
1205 40 : if (mapped_name == NULL) {
1206 0 : ok = false;
1207 0 : goto fail;
1208 : }
1209 :
1210 40 : stream_name = synthetic_smb_fname(talloc_tos(),
1211 40 : smb_fname->base_name,
1212 : mapped_name,
1213 : NULL,
1214 40 : smb_fname->twrp,
1215 40 : smb_fname->flags);
1216 40 : TALLOC_FREE(mapped_name);
1217 40 : if (stream_name == NULL) {
1218 0 : DBG_ERR("synthetic_smb_fname failed\n");
1219 0 : ok = false;
1220 0 : goto fail;
1221 : }
1222 :
1223 40 : DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1224 :
1225 40 : status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1226 40 : if (!NT_STATUS_IS_OK(status) &&
1227 32 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1228 : {
1229 0 : ok = false;
1230 0 : goto fail;
1231 : }
1232 :
1233 40 : status = SMB_VFS_CREATE_FILE(
1234 : handle->conn, /* conn */
1235 : NULL, /* req */
1236 : NULL, /* dirfsp */
1237 : stream_name, /* fname */
1238 : FILE_GENERIC_WRITE, /* access_mask */
1239 : FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /* share_access */
1240 : FILE_OPEN_IF, /* create_disposition */
1241 : 0, /* create_options */
1242 : 0, /* file_attributes */
1243 : INTERNAL_OPEN_ONLY, /* oplock_request */
1244 : NULL, /* lease */
1245 : 0, /* allocation_size */
1246 : 0, /* private_flags */
1247 : NULL, /* sd */
1248 : NULL, /* ea_list */
1249 : &fsp, /* result */
1250 : NULL, /* psbuf */
1251 : NULL, NULL); /* create context */
1252 40 : TALLOC_FREE(stream_name);
1253 40 : if (!NT_STATUS_IS_OK(status)) {
1254 0 : DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1255 0 : ok = false;
1256 0 : goto fail;
1257 : }
1258 :
1259 40 : nwritten = SMB_VFS_PWRITE(fsp,
1260 : ad->ad_data + e->adx_offset,
1261 : e->adx_length,
1262 : 0);
1263 40 : if (nwritten == -1) {
1264 0 : DBG_ERR("SMB_VFS_PWRITE failed\n");
1265 0 : saved_errno = errno;
1266 0 : close_file_free(NULL, &fsp, ERROR_CLOSE);
1267 0 : errno = saved_errno;
1268 0 : ok = false;
1269 0 : goto fail;
1270 : }
1271 :
1272 40 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1273 40 : if (!NT_STATUS_IS_OK(status)) {
1274 0 : ok = false;
1275 0 : goto fail;
1276 : }
1277 40 : fsp = NULL;
1278 : }
1279 :
1280 28 : ad->adx_header.adx_num_attrs = 0;
1281 28 : TALLOC_FREE(ad->adx_entries);
1282 :
1283 28 : ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1284 :
1285 28 : rc = ad_fset(handle, ad, ad->ad_fsp);
1286 28 : if (rc != 0) {
1287 0 : DBG_ERR("ad_fset on [%s] failed: %s\n",
1288 : fsp_str_dbg(ad->ad_fsp), strerror(errno));
1289 0 : ok = false;
1290 0 : goto fail;
1291 : }
1292 :
1293 28 : ok = ad_convert_move_reso(handle, ad, smb_fname);
1294 28 : if (!ok) {
1295 0 : goto fail;
1296 : }
1297 :
1298 28 : *converted_xattr = true;
1299 28 : ok = true;
1300 :
1301 28 : fail:
1302 28 : return ok;
1303 : }
1304 :
1305 516 : static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1306 : struct adouble *ad,
1307 : const struct smb_filename *smb_fname)
1308 : {
1309 516 : char *p_ad = NULL;
1310 516 : AfpInfo *ai = NULL;
1311 0 : DATA_BLOB aiblob;
1312 516 : struct smb_filename *stream_name = NULL;
1313 516 : files_struct *fsp = NULL;
1314 0 : size_t size;
1315 0 : ssize_t nwritten;
1316 0 : NTSTATUS status;
1317 516 : int saved_errno = 0;
1318 0 : int cmp;
1319 :
1320 516 : cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1321 516 : if (cmp != 0) {
1322 446 : return true;
1323 : }
1324 :
1325 70 : p_ad = ad_get_entry(ad, ADEID_FINDERI);
1326 70 : if (p_ad == NULL) {
1327 0 : return false;
1328 : }
1329 :
1330 70 : ai = afpinfo_new(talloc_tos());
1331 70 : if (ai == NULL) {
1332 0 : return false;
1333 : }
1334 :
1335 70 : memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1336 :
1337 70 : aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1338 70 : if (aiblob.data == NULL) {
1339 0 : TALLOC_FREE(ai);
1340 0 : return false;
1341 : }
1342 :
1343 70 : size = afpinfo_pack(ai, (char *)aiblob.data);
1344 70 : TALLOC_FREE(ai);
1345 70 : if (size != AFP_INFO_SIZE) {
1346 0 : return false;
1347 : }
1348 :
1349 70 : stream_name = synthetic_smb_fname(talloc_tos(),
1350 70 : smb_fname->base_name,
1351 : AFPINFO_STREAM,
1352 : NULL,
1353 70 : smb_fname->twrp,
1354 70 : smb_fname->flags);
1355 70 : if (stream_name == NULL) {
1356 0 : data_blob_free(&aiblob);
1357 0 : DBG_ERR("synthetic_smb_fname failed\n");
1358 0 : return false;
1359 : }
1360 :
1361 70 : DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1362 :
1363 70 : status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
1364 70 : if (!NT_STATUS_IS_OK(status) &&
1365 28 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
1366 : {
1367 0 : return false;
1368 : }
1369 :
1370 70 : status = SMB_VFS_CREATE_FILE(
1371 : handle->conn, /* conn */
1372 : NULL, /* req */
1373 : NULL, /* dirfsp */
1374 : stream_name, /* fname */
1375 : FILE_GENERIC_WRITE, /* access_mask */
1376 : FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1377 : FILE_OPEN_IF, /* create_disposition */
1378 : 0, /* create_options */
1379 : 0, /* file_attributes */
1380 : INTERNAL_OPEN_ONLY, /* oplock_request */
1381 : NULL, /* lease */
1382 : 0, /* allocation_size */
1383 : 0, /* private_flags */
1384 : NULL, /* sd */
1385 : NULL, /* ea_list */
1386 : &fsp, /* result */
1387 : NULL, /* psbuf */
1388 : NULL, NULL); /* create context */
1389 70 : TALLOC_FREE(stream_name);
1390 70 : if (!NT_STATUS_IS_OK(status)) {
1391 6 : DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1392 6 : return false;
1393 : }
1394 :
1395 64 : nwritten = SMB_VFS_PWRITE(fsp,
1396 : aiblob.data,
1397 : aiblob.length,
1398 : 0);
1399 64 : if (nwritten == -1) {
1400 0 : DBG_ERR("SMB_VFS_PWRITE failed\n");
1401 0 : saved_errno = errno;
1402 0 : close_file_free(NULL, &fsp, ERROR_CLOSE);
1403 0 : errno = saved_errno;
1404 0 : return false;
1405 : }
1406 :
1407 64 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1408 64 : if (!NT_STATUS_IS_OK(status)) {
1409 0 : return false;
1410 : }
1411 64 : fsp = NULL;
1412 :
1413 64 : return true;
1414 : }
1415 :
1416 28 : static bool ad_convert_truncate(vfs_handle_struct *handle,
1417 : struct adouble *ad,
1418 : const struct smb_filename *smb_fname)
1419 : {
1420 0 : int rc;
1421 0 : off_t newlen;
1422 :
1423 28 : newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1424 :
1425 28 : rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1426 28 : if (rc != 0) {
1427 0 : return false;
1428 : }
1429 :
1430 28 : return true;
1431 : }
1432 :
1433 516 : static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1434 : struct adouble *ad,
1435 : uint32_t flags,
1436 : bool *blank)
1437 516 : {
1438 516 : size_t rforklen = sizeof(empty_resourcefork);
1439 516 : char buf[rforklen];
1440 0 : ssize_t nread;
1441 0 : int cmp;
1442 0 : int rc;
1443 :
1444 516 : *blank = false;
1445 :
1446 516 : if (!(flags & AD_CONV_WIPE_BLANK)) {
1447 362 : return true;
1448 : }
1449 :
1450 154 : if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1451 126 : return true;
1452 : }
1453 :
1454 28 : nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1455 28 : if (nread != rforklen) {
1456 0 : DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1457 : rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1458 0 : return false;
1459 : }
1460 :
1461 28 : cmp = memcmp(buf, empty_resourcefork, rforklen);
1462 28 : if (cmp != 0) {
1463 20 : return true;
1464 : }
1465 :
1466 8 : ad_setentrylen(ad, ADEID_RFORK, 0);
1467 :
1468 8 : rc = ad_fset(handle, ad, ad->ad_fsp);
1469 8 : if (rc != 0) {
1470 0 : DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1471 0 : return false;
1472 : }
1473 :
1474 8 : *blank = true;
1475 8 : return true;
1476 : }
1477 :
1478 510 : static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1479 : struct adouble *ad,
1480 : const struct smb_filename *smb_fname,
1481 : uint32_t flags)
1482 : {
1483 510 : struct smb_filename *parent_fname = NULL;
1484 510 : struct smb_filename *at_fname = NULL;
1485 510 : struct smb_filename *ad_name = NULL;
1486 0 : NTSTATUS status;
1487 0 : int rc;
1488 :
1489 510 : if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1490 394 : return true;
1491 : }
1492 :
1493 116 : if (!(flags & AD_CONV_DELETE)) {
1494 96 : return true;
1495 : }
1496 :
1497 20 : rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1498 20 : if (rc != 0) {
1499 0 : return false;
1500 : }
1501 :
1502 20 : status = parent_pathref(talloc_tos(),
1503 20 : handle->conn->cwd_fsp,
1504 : ad_name,
1505 : &parent_fname,
1506 : &at_fname);
1507 20 : TALLOC_FREE(ad_name);
1508 20 : if (!NT_STATUS_IS_OK(status)) {
1509 0 : return false;
1510 : }
1511 :
1512 20 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
1513 : parent_fname->fsp,
1514 : at_fname,
1515 : 0);
1516 20 : if (rc != 0) {
1517 0 : DBG_ERR("Unlinking [%s/%s] failed: %s\n",
1518 : smb_fname_str_dbg(parent_fname),
1519 : smb_fname_str_dbg(at_fname), strerror(errno));
1520 0 : TALLOC_FREE(parent_fname);
1521 0 : return false;
1522 : }
1523 :
1524 20 : DBG_WARNING("Unlinked [%s/%s] after conversion\n",
1525 : smb_fname_str_dbg(parent_fname),
1526 : smb_fname_str_dbg(at_fname));
1527 20 : TALLOC_FREE(parent_fname);
1528 :
1529 20 : return true;
1530 : }
1531 :
1532 : /**
1533 : * Convert from Apple's ._ file to Netatalk
1534 : *
1535 : * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1536 : * bytes containing packed xattrs.
1537 : *
1538 : * @return -1 in case an error occurred, 0 if no conversion was done, 1
1539 : * otherwise
1540 : **/
1541 3796 : int ad_convert(struct vfs_handle_struct *handle,
1542 : const struct smb_filename *smb_fname,
1543 : const char *catia_mappings,
1544 : uint32_t flags)
1545 : {
1546 3796 : struct adouble *ad = NULL;
1547 0 : bool ok;
1548 3796 : bool converted_xattr = false;
1549 0 : bool blank;
1550 0 : int ret;
1551 :
1552 3796 : ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1553 3796 : if (ad == NULL) {
1554 3280 : return 0;
1555 : }
1556 :
1557 516 : ok = ad_convert_xattr(handle,
1558 : ad,
1559 : smb_fname,
1560 : catia_mappings,
1561 : &converted_xattr);
1562 516 : if (!ok) {
1563 0 : ret = -1;
1564 0 : goto done;
1565 : }
1566 :
1567 516 : ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
1568 516 : if (!ok) {
1569 0 : ret = -1;
1570 0 : goto done;
1571 : }
1572 :
1573 516 : if (converted_xattr || blank) {
1574 28 : ok = ad_convert_truncate(handle, ad, smb_fname);
1575 28 : if (!ok) {
1576 0 : ret = -1;
1577 0 : goto done;
1578 : }
1579 : }
1580 :
1581 516 : ok = ad_convert_finderinfo(handle, ad, smb_fname);
1582 516 : if (!ok) {
1583 6 : DBG_ERR("Failed to convert [%s]\n",
1584 : smb_fname_str_dbg(smb_fname));
1585 6 : ret = -1;
1586 6 : goto done;
1587 : }
1588 :
1589 510 : ok = ad_convert_delete_adfile(handle,
1590 : ad,
1591 : smb_fname,
1592 : flags);
1593 510 : if (!ok) {
1594 0 : ret = -1;
1595 0 : goto done;
1596 : }
1597 :
1598 510 : ret = 0;
1599 516 : done:
1600 516 : TALLOC_FREE(ad);
1601 516 : return ret;
1602 : }
1603 :
1604 6 : static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx,
1605 : struct vfs_handle_struct *handle,
1606 : struct smb_filename *smb_fname,
1607 : struct smb_filename *adpath,
1608 : files_struct **_fsp)
1609 : {
1610 6 : files_struct *fsp = NULL;
1611 0 : NTSTATUS status;
1612 0 : int ret;
1613 :
1614 6 : status = openat_pathref_fsp(handle->conn->cwd_fsp, adpath);
1615 6 : if (!NT_STATUS_IS_OK(status) &&
1616 2 : !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1617 0 : return false;
1618 : }
1619 :
1620 6 : status = SMB_VFS_CREATE_FILE(
1621 : handle->conn,
1622 : NULL, /* req */
1623 : NULL, /* dirfsp */
1624 : adpath,
1625 : FILE_READ_DATA|FILE_WRITE_DATA,
1626 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1627 : FILE_OPEN_IF,
1628 : 0, /* create_options */
1629 : 0, /* file_attributes */
1630 : INTERNAL_OPEN_ONLY,
1631 : NULL, /* lease */
1632 : 0, /* allocation_size */
1633 : 0, /* private_flags */
1634 : NULL, /* sd */
1635 : NULL, /* ea_list */
1636 : &fsp,
1637 : NULL, /* info */
1638 : NULL, NULL); /* create context */
1639 6 : if (!NT_STATUS_IS_OK(status)) {
1640 0 : DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
1641 : smb_fname_str_dbg(adpath), nt_errstr(status));
1642 0 : return false;
1643 : }
1644 :
1645 6 : if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid ||
1646 6 : fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid)
1647 : {
1648 0 : ret = SMB_VFS_FCHOWN(fsp,
1649 : smb_fname->st.st_ex_uid,
1650 : smb_fname->st.st_ex_gid);
1651 0 : if (ret != 0) {
1652 0 : DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n",
1653 : fsp_str_dbg(fsp), nt_errstr(status));
1654 0 : close_file_free(NULL, &fsp, NORMAL_CLOSE);
1655 0 : return false;
1656 : }
1657 : }
1658 :
1659 6 : *_fsp = fsp;
1660 6 : return true;
1661 : }
1662 :
1663 12 : static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle,
1664 : struct smb_filename *smb_fname,
1665 : TALLOC_CTX *mem_ctx,
1666 : unsigned int *num_streams,
1667 : struct stream_struct **streams)
1668 : {
1669 12 : files_struct *fsp = NULL;
1670 0 : NTSTATUS status;
1671 :
1672 12 : status = openat_pathref_fsp(handle->conn->cwd_fsp, smb_fname);
1673 12 : if (!NT_STATUS_IS_OK(status)) {
1674 0 : return false;
1675 : }
1676 :
1677 12 : status = SMB_VFS_CREATE_FILE(
1678 : handle->conn, /* conn */
1679 : NULL, /* req */
1680 : NULL, /* dirfsp */
1681 : smb_fname, /* fname */
1682 : FILE_READ_ATTRIBUTES, /* access_mask */
1683 : (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1684 : FILE_SHARE_DELETE),
1685 : FILE_OPEN, /* create_disposition*/
1686 : 0, /* create_options */
1687 : 0, /* file_attributes */
1688 : INTERNAL_OPEN_ONLY, /* oplock_request */
1689 : NULL, /* lease */
1690 : 0, /* allocation_size */
1691 : 0, /* private_flags */
1692 : NULL, /* sd */
1693 : NULL, /* ea_list */
1694 : &fsp, /* result */
1695 : NULL, /* pinfo */
1696 : NULL, NULL); /* create context */
1697 12 : if (!NT_STATUS_IS_OK(status)) {
1698 0 : DBG_ERR("Opening [%s] failed: %s\n",
1699 : smb_fname_str_dbg(smb_fname),
1700 : nt_errstr(status));
1701 0 : return false;
1702 : }
1703 :
1704 12 : status = vfs_fstreaminfo(fsp,
1705 : mem_ctx,
1706 : num_streams,
1707 : streams);
1708 12 : if (!NT_STATUS_IS_OK(status)) {
1709 0 : close_file_free(NULL, &fsp, NORMAL_CLOSE);
1710 0 : DBG_ERR("streaminfo on [%s] failed: %s\n",
1711 : smb_fname_str_dbg(smb_fname),
1712 : nt_errstr(status));
1713 0 : return false;
1714 : }
1715 :
1716 12 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1717 12 : if (!NT_STATUS_IS_OK(status)) {
1718 0 : DBG_ERR("close_file [%s] failed: %s\n",
1719 : smb_fname_str_dbg(smb_fname),
1720 : nt_errstr(status));
1721 0 : return false;
1722 : }
1723 :
1724 12 : return true;
1725 : }
1726 :
1727 : struct ad_collect_state {
1728 : bool have_adfile;
1729 : size_t adx_data_off;
1730 : char *rsrc_data_buf;
1731 : };
1732 :
1733 24 : static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
1734 : struct char_mappings **cmaps,
1735 : struct smb_filename *smb_fname,
1736 : const struct stream_struct *stream,
1737 : struct adouble *ad,
1738 : struct ad_collect_state *state)
1739 : {
1740 24 : struct smb_filename *sname = NULL;
1741 24 : files_struct *fsp = NULL;
1742 24 : struct ad_xattr_entry *e = NULL;
1743 24 : char *mapped_name = NULL;
1744 24 : char *p = NULL;
1745 0 : size_t needed_size;
1746 0 : ssize_t nread;
1747 0 : NTSTATUS status;
1748 0 : bool ok;
1749 :
1750 24 : sname = synthetic_smb_fname(ad,
1751 24 : smb_fname->base_name,
1752 24 : stream->name,
1753 : NULL,
1754 : smb_fname->twrp,
1755 : 0);
1756 24 : if (sname == NULL) {
1757 0 : return false;
1758 : }
1759 :
1760 24 : if (is_ntfs_default_stream_smb_fname(sname)) {
1761 6 : TALLOC_FREE(sname);
1762 6 : return true;
1763 : }
1764 :
1765 18 : DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));
1766 :
1767 18 : status = openat_pathref_fsp(handle->conn->cwd_fsp, sname);
1768 18 : if (!NT_STATUS_IS_OK(status)) {
1769 0 : ok = false;
1770 0 : goto out;
1771 : }
1772 :
1773 18 : status = SMB_VFS_CREATE_FILE(
1774 : handle->conn,
1775 : NULL, /* req */
1776 : NULL, /* dirfsp */
1777 : sname,
1778 : FILE_READ_DATA|DELETE_ACCESS,
1779 : FILE_SHARE_READ,
1780 : FILE_OPEN,
1781 : 0, /* create_options */
1782 : 0, /* file_attributes */
1783 : INTERNAL_OPEN_ONLY, /* oplock_request */
1784 : NULL, /* lease */
1785 : 0, /* allocation_size */
1786 : 0, /* private_flags */
1787 : NULL, /* sd */
1788 : NULL, /* ea_list */
1789 : &fsp,
1790 : NULL, /* info */
1791 : NULL, NULL); /* create context */
1792 18 : if (!NT_STATUS_IS_OK(status)) {
1793 0 : DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
1794 : smb_fname_str_dbg(sname));
1795 0 : ok = false;
1796 0 : goto out;
1797 : }
1798 :
1799 18 : if (is_afpinfo_stream(stream->name)) {
1800 0 : char buf[AFP_INFO_SIZE];
1801 :
1802 6 : if (stream->size != AFP_INFO_SIZE) {
1803 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1804 : (ssize_t)stream->size,
1805 : smb_fname_str_dbg(sname));
1806 0 : ok = false;
1807 0 : goto out;
1808 : }
1809 :
1810 6 : nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
1811 6 : if (nread != AFP_INFO_SIZE) {
1812 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1813 : (ssize_t)stream->size,
1814 : smb_fname_str_dbg(sname));
1815 0 : ok = false;
1816 0 : goto out;
1817 : }
1818 :
1819 6 : memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
1820 : buf + AFP_OFF_FinderInfo,
1821 : AFP_FinderSize);
1822 :
1823 6 : ok = set_delete_on_close(fsp,
1824 : true,
1825 6 : fsp->conn->session_info->security_token,
1826 6 : fsp->conn->session_info->unix_token);
1827 6 : if (!ok) {
1828 0 : DBG_ERR("Deleting [%s] failed\n",
1829 : smb_fname_str_dbg(sname));
1830 0 : ok = false;
1831 0 : goto out;
1832 : }
1833 6 : ok = true;
1834 6 : goto out;
1835 : }
1836 :
1837 12 : if (is_afpresource_stream(stream->name)) {
1838 6 : ad->ad_rsrc_data = talloc_size(ad, stream->size);
1839 6 : if (ad->ad_rsrc_data == NULL) {
1840 0 : ok = false;
1841 0 : goto out;
1842 : }
1843 :
1844 6 : nread = SMB_VFS_PREAD(fsp,
1845 : ad->ad_rsrc_data,
1846 : stream->size,
1847 : 0);
1848 6 : if (nread != stream->size) {
1849 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1850 : (ssize_t)stream->size,
1851 : smb_fname_str_dbg(sname));
1852 0 : ok = false;
1853 0 : goto out;
1854 : }
1855 :
1856 6 : ad_setentrylen(ad, ADEID_RFORK, stream->size);
1857 :
1858 6 : if (!state->have_adfile) {
1859 : /*
1860 : * We have a resource *stream* but no AppleDouble
1861 : * sidecar file, this means the share is configured with
1862 : * fruit:resource=stream. So we should delete the
1863 : * resource stream.
1864 : */
1865 2 : ok = set_delete_on_close(
1866 : fsp,
1867 : true,
1868 2 : fsp->conn->session_info->security_token,
1869 2 : fsp->conn->session_info->unix_token);
1870 2 : if (!ok) {
1871 0 : DBG_ERR("Deleting [%s] failed\n",
1872 : smb_fname_str_dbg(sname));
1873 0 : ok = false;
1874 0 : goto out;
1875 : }
1876 : }
1877 6 : ok = true;
1878 6 : goto out;
1879 : }
1880 :
1881 6 : ad->adx_entries = talloc_realloc(ad,
1882 : ad->adx_entries,
1883 : struct ad_xattr_entry,
1884 : ad->adx_header.adx_num_attrs + 1);
1885 6 : if (ad->adx_entries == NULL) {
1886 0 : ok = false;
1887 0 : goto out;
1888 : }
1889 :
1890 6 : e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
1891 6 : *e = (struct ad_xattr_entry) {
1892 6 : .adx_length = stream->size,
1893 : };
1894 6 : e->adx_name = talloc_strdup(ad, stream->name + 1);
1895 6 : if (e->adx_name == NULL) {
1896 0 : ok = false;
1897 0 : goto out;
1898 : }
1899 6 : p = strchr(e->adx_name, ':');
1900 6 : if (p != NULL) {
1901 6 : *p = '\0';
1902 : }
1903 :
1904 6 : status = string_replace_allocate(handle->conn,
1905 6 : e->adx_name,
1906 : cmaps,
1907 : ad,
1908 : &mapped_name,
1909 : vfs_translate_to_unix);
1910 6 : if (!NT_STATUS_IS_OK(status) &&
1911 0 : !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1912 : {
1913 0 : DBG_ERR("string_replace_allocate failed\n");
1914 0 : ok = false;
1915 0 : goto out;
1916 : }
1917 :
1918 6 : e->adx_name = mapped_name;
1919 6 : e->adx_namelen = strlen(e->adx_name) + 1,
1920 :
1921 6 : DBG_DEBUG("%u: name (%s) size (%zu)\n",
1922 : ad->adx_header.adx_num_attrs,
1923 : e->adx_name,
1924 : (size_t)e->adx_length);
1925 :
1926 6 : ad->adx_header.adx_num_attrs++;
1927 :
1928 6 : needed_size = state->adx_data_off + stream->size;
1929 6 : if (needed_size > talloc_get_size(ad->adx_data)) {
1930 6 : ad->adx_data = talloc_realloc(ad,
1931 : ad->adx_data,
1932 : char,
1933 : needed_size);
1934 6 : if (ad->adx_data == NULL) {
1935 0 : ok = false;
1936 0 : goto out;
1937 : }
1938 : }
1939 :
1940 6 : nread = SMB_VFS_PREAD(fsp,
1941 : ad->adx_data + state->adx_data_off,
1942 : stream->size,
1943 : 0);
1944 6 : if (nread != stream->size) {
1945 0 : DBG_ERR("Bad size [%zd] on [%s]\n",
1946 : (ssize_t)stream->size,
1947 : smb_fname_str_dbg(sname));
1948 0 : ok = false;
1949 0 : goto out;
1950 : }
1951 6 : state->adx_data_off += nread;
1952 :
1953 6 : ok = set_delete_on_close(fsp,
1954 : true,
1955 6 : fsp->conn->session_info->security_token,
1956 6 : fsp->conn->session_info->unix_token);
1957 6 : if (!ok) {
1958 0 : DBG_ERR("Deleting [%s] failed\n",
1959 : smb_fname_str_dbg(sname));
1960 0 : ok = false;
1961 0 : goto out;
1962 : }
1963 :
1964 6 : out:
1965 18 : TALLOC_FREE(sname);
1966 18 : if (fsp != NULL) {
1967 18 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
1968 18 : if (!NT_STATUS_IS_OK(status)) {
1969 0 : DBG_ERR("close_file [%s] failed: %s\n",
1970 : smb_fname_str_dbg(smb_fname),
1971 : nt_errstr(status));
1972 0 : ok = false;
1973 : }
1974 : }
1975 :
1976 18 : return ok;
1977 : }
1978 :
1979 : /**
1980 : * Convert filesystem metadata to AppleDouble file
1981 : **/
1982 12 : bool ad_unconvert(TALLOC_CTX *mem_ctx,
1983 : struct vfs_handle_struct *handle,
1984 : const char *catia_mappings,
1985 : struct smb_filename *smb_fname,
1986 : bool *converted)
1987 : {
1988 0 : static struct char_mappings **cmaps = NULL;
1989 12 : TALLOC_CTX *frame = talloc_stackframe();
1990 0 : struct ad_collect_state state;
1991 12 : struct stream_struct *streams = NULL;
1992 12 : struct smb_filename *adpath = NULL;
1993 12 : struct adouble *ad = NULL;
1994 12 : unsigned int num_streams = 0;
1995 12 : size_t to_convert = 0;
1996 12 : bool have_rsrc = false;
1997 12 : files_struct *fsp = NULL;
1998 0 : size_t i;
1999 0 : NTSTATUS status;
2000 0 : int ret;
2001 0 : bool ok;
2002 :
2003 12 : *converted = false;
2004 :
2005 12 : if (cmaps == NULL) {
2006 6 : const char **mappings = NULL;
2007 :
2008 6 : mappings = str_list_make_v3_const(
2009 : frame, catia_mappings, NULL);
2010 6 : if (mappings == NULL) {
2011 0 : ok = false;
2012 0 : goto out;
2013 : }
2014 6 : cmaps = string_replace_init_map(mem_ctx, mappings);
2015 6 : TALLOC_FREE(mappings);
2016 : }
2017 :
2018 12 : ok = ad_unconvert_get_streams(handle,
2019 : smb_fname,
2020 : frame,
2021 : &num_streams,
2022 : &streams);
2023 12 : if (!ok) {
2024 0 : goto out;
2025 : }
2026 :
2027 36 : for (i = 0; i < num_streams; i++) {
2028 24 : if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
2029 6 : continue;
2030 : }
2031 18 : to_convert++;
2032 18 : if (is_afpresource_stream(streams[i].name)) {
2033 6 : have_rsrc = true;
2034 : }
2035 : }
2036 :
2037 12 : if (to_convert == 0) {
2038 6 : ok = true;
2039 6 : goto out;
2040 : }
2041 :
2042 6 : state = (struct ad_collect_state) {
2043 : .adx_data_off = 0,
2044 : };
2045 :
2046 6 : ret = adouble_path(frame, smb_fname, &adpath);
2047 6 : if (ret != 0) {
2048 0 : ok = false;
2049 0 : goto out;
2050 : }
2051 :
2052 6 : ret = SMB_VFS_STAT(handle->conn, adpath);
2053 6 : if (ret == 0) {
2054 4 : state.have_adfile = true;
2055 : } else {
2056 2 : if (errno != ENOENT) {
2057 0 : ok = false;
2058 0 : goto out;
2059 : }
2060 2 : state.have_adfile = false;
2061 : }
2062 :
2063 6 : if (to_convert == 1 && have_rsrc && state.have_adfile) {
2064 : /*
2065 : * So we have just a single stream, the resource fork stream
2066 : * from an AppleDouble file. Fine, that means there's nothing to
2067 : * convert.
2068 : */
2069 0 : ok = true;
2070 0 : goto out;
2071 : }
2072 :
2073 6 : ad = ad_init(frame, ADOUBLE_RSRC);
2074 6 : if (ad == NULL) {
2075 0 : ok = false;
2076 0 : goto out;
2077 : }
2078 :
2079 30 : for (i = 0; i < num_streams; i++) {
2080 24 : ok = ad_collect_one_stream(handle,
2081 : cmaps,
2082 : smb_fname,
2083 24 : &streams[i],
2084 : ad,
2085 : &state);
2086 24 : if (!ok) {
2087 0 : goto out;
2088 : }
2089 : }
2090 :
2091 6 : ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
2092 6 : if (!ok) {
2093 0 : DBG_ERR("Failed to open adfile [%s]\n",
2094 : smb_fname_str_dbg(smb_fname));
2095 0 : goto out;
2096 : }
2097 :
2098 6 : ret = ad_fset(handle, ad, fsp);
2099 6 : if (ret != 0) {
2100 0 : ok = false;
2101 0 : goto out;
2102 : }
2103 :
2104 6 : *converted = true;
2105 6 : ok = true;
2106 :
2107 12 : out:
2108 12 : if (fsp != NULL) {
2109 6 : status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
2110 6 : if (!NT_STATUS_IS_OK(status)) {
2111 0 : DBG_ERR("close_file_free() [%s] failed: %s\n",
2112 : smb_fname_str_dbg(smb_fname),
2113 : nt_errstr(status));
2114 0 : ok = false;
2115 : }
2116 : }
2117 12 : TALLOC_FREE(frame);
2118 12 : return ok;
2119 : }
2120 :
2121 : /**
2122 : * Read and parse Netatalk AppleDouble metadata xattr
2123 : **/
2124 25238 : static ssize_t ad_read_meta(vfs_handle_struct *handle,
2125 : struct adouble *ad,
2126 : const struct smb_filename *smb_fname)
2127 : {
2128 25238 : int rc = 0;
2129 0 : ssize_t ealen;
2130 0 : bool ok;
2131 25238 : struct files_struct *fsp = smb_fname->fsp;
2132 :
2133 25238 : DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
2134 :
2135 25238 : fsp = metadata_fsp(fsp);
2136 :
2137 25238 : ealen = SMB_VFS_FGETXATTR(fsp,
2138 : AFPINFO_EA_NETATALK,
2139 : ad->ad_data,
2140 : AD_DATASZ_XATTR);
2141 :
2142 25238 : if (ealen == -1) {
2143 24020 : switch (errno) {
2144 24020 : case ENOATTR:
2145 : case ENOENT:
2146 24020 : if (errno == ENOATTR) {
2147 24020 : errno = ENOENT;
2148 : }
2149 24020 : rc = -1;
2150 24020 : goto exit;
2151 0 : default:
2152 0 : DEBUG(2, ("error reading meta xattr: %s\n",
2153 : strerror(errno)));
2154 0 : rc = -1;
2155 0 : goto exit;
2156 : }
2157 : }
2158 1218 : if (ealen != AD_DATASZ_XATTR) {
2159 0 : DEBUG(2, ("bad size %zd\n", ealen));
2160 0 : errno = EINVAL;
2161 0 : rc = -1;
2162 0 : goto exit;
2163 : }
2164 :
2165 : /* Now parse entries */
2166 1218 : ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
2167 1218 : if (!ok) {
2168 0 : DBG_WARNING(
2169 : "Invalid AppleDouble xattr metadata (%s) in file: %s - "
2170 : "Consider deleting the corrupted file.\n",
2171 : smb_fname->base_name,
2172 : ad->ad_fsp->fsp_name->base_name);
2173 0 : errno = EINVAL;
2174 0 : rc = -1;
2175 0 : goto exit;
2176 : }
2177 :
2178 1218 : if (!ad_getentryoff(ad, ADEID_FINDERI)
2179 1218 : || !ad_getentryoff(ad, ADEID_COMMENT)
2180 1218 : || !ad_getentryoff(ad, ADEID_FILEDATESI)
2181 1218 : || !ad_getentryoff(ad, ADEID_AFPFILEI)
2182 1218 : || !ad_getentryoff(ad, ADEID_PRIVDEV)
2183 1218 : || !ad_getentryoff(ad, ADEID_PRIVINO)
2184 1218 : || !ad_getentryoff(ad, ADEID_PRIVSYN)
2185 1218 : || !ad_getentryoff(ad, ADEID_PRIVID)) {
2186 0 : DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
2187 0 : errno = EINVAL;
2188 0 : rc = -1;
2189 0 : goto exit;
2190 : }
2191 :
2192 1218 : exit:
2193 25238 : DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
2194 : smb_fname->base_name, rc));
2195 :
2196 25238 : if (rc != 0) {
2197 24020 : ealen = -1;
2198 24020 : if (errno == EINVAL) {
2199 0 : become_root();
2200 0 : (void)SMB_VFS_FREMOVEXATTR(fsp,
2201 : AFPINFO_EA_NETATALK);
2202 0 : unbecome_root();
2203 0 : errno = ENOENT;
2204 : }
2205 : }
2206 25238 : return ealen;
2207 : }
2208 :
2209 9214 : static NTSTATUS adouble_open_rsrc_fsp(const struct files_struct *dirfsp,
2210 : const struct smb_filename *smb_base_fname,
2211 : int in_flags,
2212 : mode_t mode,
2213 : struct files_struct **_ad_fsp)
2214 : {
2215 9214 : int rc = 0;
2216 9214 : struct adouble *ad = NULL;
2217 9214 : struct smb_filename *adp_smb_fname = NULL;
2218 9214 : struct files_struct *ad_fsp = NULL;
2219 0 : NTSTATUS status;
2220 9214 : struct vfs_open_how how = { .flags = in_flags, .mode = mode, };
2221 :
2222 9214 : rc = adouble_path(talloc_tos(),
2223 : smb_base_fname,
2224 : &adp_smb_fname);
2225 9214 : if (rc != 0) {
2226 0 : return NT_STATUS_NO_MEMORY;
2227 : }
2228 :
2229 9214 : status = create_internal_fsp(dirfsp->conn,
2230 : adp_smb_fname,
2231 : &ad_fsp);
2232 9214 : if (!NT_STATUS_IS_OK(status)) {
2233 0 : return status;
2234 : }
2235 :
2236 : #ifdef O_PATH
2237 4607 : how.flags &= ~(O_PATH);
2238 : #endif
2239 9214 : if (how.flags & (O_CREAT | O_TRUNC | O_WRONLY)) {
2240 : /* We always need read/write access for the metadata header too */
2241 144 : how.flags &= ~(O_WRONLY);
2242 144 : how.flags |= O_RDWR;
2243 : }
2244 :
2245 9214 : status = fd_openat(dirfsp,
2246 : adp_smb_fname,
2247 : ad_fsp,
2248 : &how);
2249 9214 : if (!NT_STATUS_IS_OK(status)) {
2250 7326 : file_free(NULL, ad_fsp);
2251 7326 : return status;
2252 : }
2253 :
2254 1888 : if (how.flags & (O_CREAT | O_TRUNC)) {
2255 138 : ad = ad_init(talloc_tos(), ADOUBLE_RSRC);
2256 138 : if (ad == NULL) {
2257 0 : file_free(NULL, ad_fsp);
2258 0 : return NT_STATUS_NO_MEMORY;
2259 : }
2260 :
2261 138 : rc = ad_fset(ad_fsp->conn->vfs_handles, ad, ad_fsp);
2262 138 : if (rc != 0) {
2263 0 : file_free(NULL, ad_fsp);
2264 0 : return NT_STATUS_IO_DEVICE_ERROR;
2265 : }
2266 138 : TALLOC_FREE(ad);
2267 : }
2268 :
2269 1888 : *_ad_fsp = ad_fsp;
2270 1888 : return NT_STATUS_OK;
2271 : }
2272 :
2273 1012 : NTSTATUS adouble_open_from_base_fsp(const struct files_struct *dirfsp,
2274 : struct files_struct *base_fsp,
2275 : adouble_type_t type,
2276 : int flags,
2277 : mode_t mode,
2278 : struct files_struct **_ad_fsp)
2279 : {
2280 1012 : *_ad_fsp = NULL;
2281 :
2282 1012 : SMB_ASSERT(base_fsp != NULL);
2283 1012 : SMB_ASSERT(!fsp_is_alternate_stream(base_fsp));
2284 :
2285 1012 : switch (type) {
2286 0 : case ADOUBLE_META:
2287 0 : return NT_STATUS_INTERNAL_ERROR;
2288 1012 : case ADOUBLE_RSRC:
2289 1012 : return adouble_open_rsrc_fsp(dirfsp,
2290 1012 : base_fsp->fsp_name,
2291 : flags,
2292 : mode,
2293 : _ad_fsp);
2294 : }
2295 :
2296 0 : return NT_STATUS_INTERNAL_ERROR;
2297 : }
2298 :
2299 : /*
2300 : * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
2301 : * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
2302 : * for file IO on the ._ file.
2303 : */
2304 34642 : static int ad_open(vfs_handle_struct *handle,
2305 : struct adouble *ad,
2306 : files_struct *fsp,
2307 : const struct smb_filename *smb_fname,
2308 : int flags,
2309 : mode_t mode)
2310 : {
2311 0 : NTSTATUS status;
2312 :
2313 34642 : DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
2314 : ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2315 :
2316 34642 : if (ad->ad_type == ADOUBLE_META) {
2317 25238 : return 0;
2318 : }
2319 :
2320 9404 : if (fsp != NULL) {
2321 1202 : ad->ad_fsp = fsp;
2322 1202 : ad->ad_opened = false;
2323 1202 : return 0;
2324 : }
2325 :
2326 8202 : status = adouble_open_rsrc_fsp(handle->conn->cwd_fsp,
2327 : smb_fname,
2328 : flags,
2329 : mode,
2330 8202 : &ad->ad_fsp);
2331 8202 : if (!NT_STATUS_IS_OK(status)) {
2332 7136 : errno = map_errno_from_nt_status(status);
2333 7136 : return -1;
2334 : }
2335 1066 : ad->ad_opened = true;
2336 :
2337 1066 : DBG_DEBUG("Path [%s] type [%s]\n",
2338 : smb_fname->base_name,
2339 : ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
2340 :
2341 1066 : return 0;
2342 : }
2343 :
2344 2268 : static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
2345 : struct adouble *ad,
2346 : const struct smb_filename *smb_fname)
2347 : {
2348 0 : size_t to_read;
2349 0 : ssize_t len;
2350 0 : int ret;
2351 0 : bool ok;
2352 :
2353 2268 : ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
2354 2268 : if (ret != 0) {
2355 0 : DBG_ERR("fstat [%s] failed: %s\n",
2356 : fsp_str_dbg(ad->ad_fsp), strerror(errno));
2357 0 : return -1;
2358 : }
2359 :
2360 2268 : to_read = ad->ad_fsp->fsp_name->st.st_ex_size;
2361 2268 : if (to_read > AD_XATTR_MAX_HDR_SIZE) {
2362 72 : to_read = AD_XATTR_MAX_HDR_SIZE;
2363 : }
2364 :
2365 2268 : len = SMB_VFS_NEXT_PREAD(handle,
2366 : ad->ad_fsp,
2367 : ad->ad_data,
2368 : to_read,
2369 : 0);
2370 2268 : if (len != to_read) {
2371 0 : DBG_NOTICE("%s %s: bad size: %zd\n",
2372 : smb_fname->base_name, strerror(errno), len);
2373 0 : return -1;
2374 : }
2375 :
2376 : /* Now parse entries */
2377 2268 : ok = ad_unpack(ad,
2378 : ADEID_NUM_DOT_UND,
2379 2268 : ad->ad_fsp->fsp_name->st.st_ex_size);
2380 2268 : if (!ok) {
2381 0 : DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2382 : "Consider deleting the corrupted file.\n",
2383 : smb_fname->base_name,
2384 : ad->ad_fsp->fsp_name->base_name);
2385 0 : errno = EINVAL;
2386 0 : return -1;
2387 : }
2388 :
2389 2268 : if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
2390 2268 : || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
2391 2268 : || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND))
2392 : {
2393 0 : DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - "
2394 : "Consider deleting the corrupted file.\n",
2395 : smb_fname->base_name,
2396 : ad->ad_fsp->fsp_name->base_name);
2397 0 : errno = EINVAL;
2398 0 : return -1;
2399 : }
2400 :
2401 2268 : return len;
2402 : }
2403 :
2404 : /**
2405 : * Read and parse resource fork, either ._ AppleDouble file or xattr
2406 : **/
2407 2268 : static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
2408 : struct adouble *ad,
2409 : const struct smb_filename *smb_fname)
2410 : {
2411 2268 : return ad_read_rsrc_adouble(handle, ad, smb_fname);
2412 : }
2413 :
2414 : /**
2415 : * Read and unpack an AppleDouble metadata xattr or resource
2416 : **/
2417 27506 : static ssize_t ad_read(vfs_handle_struct *handle,
2418 : struct adouble *ad,
2419 : const struct smb_filename *smb_fname)
2420 : {
2421 27506 : switch (ad->ad_type) {
2422 25238 : case ADOUBLE_META:
2423 25238 : return ad_read_meta(handle, ad, smb_fname);
2424 2268 : case ADOUBLE_RSRC:
2425 2268 : return ad_read_rsrc(handle, ad, smb_fname);
2426 0 : default:
2427 0 : return -1;
2428 : }
2429 : }
2430 :
2431 34908 : static int adouble_destructor(struct adouble *ad)
2432 : {
2433 0 : NTSTATUS status;
2434 :
2435 34908 : if (!ad->ad_opened) {
2436 33842 : return 0;
2437 : }
2438 :
2439 1066 : SMB_ASSERT(ad->ad_fsp != NULL);
2440 :
2441 1066 : status = fd_close(ad->ad_fsp);
2442 1066 : if (!NT_STATUS_IS_OK(status)) {
2443 0 : DBG_ERR("Closing [%s] failed: %s\n",
2444 : fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
2445 : }
2446 1066 : file_free(NULL, ad->ad_fsp);
2447 1066 : ad->ad_fsp = NULL;
2448 1066 : ad->ad_opened = false;
2449 :
2450 1066 : return 0;
2451 : }
2452 :
2453 : /**
2454 : * Allocate a struct adouble without initialiing it
2455 : *
2456 : * The struct is either hang of the fsp extension context or if fsp is
2457 : * NULL from ctx.
2458 : *
2459 : * @param[in] ctx talloc context
2460 : * @param[in] handle vfs handle
2461 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2462 : *
2463 : * @return adouble handle
2464 : **/
2465 34908 : static struct adouble *ad_alloc(TALLOC_CTX *ctx,
2466 : adouble_type_t type)
2467 : {
2468 34908 : int rc = 0;
2469 34908 : size_t adsize = 0;
2470 0 : struct adouble *ad;
2471 :
2472 34908 : switch (type) {
2473 25360 : case ADOUBLE_META:
2474 25360 : adsize = AD_DATASZ_XATTR;
2475 25360 : break;
2476 9548 : case ADOUBLE_RSRC:
2477 : /*
2478 : * AppleDouble ._ file case, optimize for fewer (but larger)
2479 : * IOs. Two cases:
2480 : *
2481 : * - without xattrs size of the header is exactly
2482 : * AD_DATASZ_DOT_UND (82) bytes
2483 : *
2484 : * - with embedded xattrs it can be larger, up to
2485 : * AD_XATTR_MAX_HDR_SIZE
2486 : *
2487 : * Larger headers are not supported, but this is a reasonable
2488 : * limit that is also employed by the macOS client.
2489 : *
2490 : * We used the largest possible size to be able to read the full
2491 : * header with one IO.
2492 : */
2493 9548 : adsize = AD_XATTR_MAX_HDR_SIZE;
2494 9548 : break;
2495 0 : default:
2496 0 : return NULL;
2497 : }
2498 :
2499 34908 : ad = talloc_zero(ctx, struct adouble);
2500 34908 : if (ad == NULL) {
2501 0 : rc = -1;
2502 0 : goto exit;
2503 : }
2504 :
2505 34908 : if (adsize) {
2506 34908 : ad->ad_data = talloc_zero_array(ad, char, adsize);
2507 34908 : if (ad->ad_data == NULL) {
2508 0 : rc = -1;
2509 0 : goto exit;
2510 : }
2511 : }
2512 :
2513 34908 : ad->ad_type = type;
2514 34908 : ad->ad_magic = AD_MAGIC;
2515 34908 : ad->ad_version = AD_VERSION;
2516 :
2517 34908 : talloc_set_destructor(ad, adouble_destructor);
2518 :
2519 34908 : exit:
2520 34908 : if (rc != 0) {
2521 0 : TALLOC_FREE(ad);
2522 : }
2523 34908 : return ad;
2524 : }
2525 :
2526 : /**
2527 : * Allocate and initialize a new struct adouble
2528 : *
2529 : * @param[in] ctx talloc context
2530 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2531 : *
2532 : * @return adouble handle, initialized
2533 : **/
2534 266 : struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
2535 : {
2536 266 : int rc = 0;
2537 0 : const struct ad_entry_order *eid;
2538 266 : struct adouble *ad = NULL;
2539 266 : time_t t = time(NULL);
2540 :
2541 266 : switch (type) {
2542 122 : case ADOUBLE_META:
2543 122 : eid = entry_order_meta_xattr;
2544 122 : break;
2545 144 : case ADOUBLE_RSRC:
2546 144 : eid = entry_order_dot_und;
2547 144 : break;
2548 0 : default:
2549 0 : return NULL;
2550 : }
2551 :
2552 266 : ad = ad_alloc(ctx, type);
2553 266 : if (ad == NULL) {
2554 0 : return NULL;
2555 : }
2556 :
2557 1530 : while (eid->id) {
2558 1264 : ad->ad_eid[eid->id].ade_off = eid->offset;
2559 1264 : ad->ad_eid[eid->id].ade_len = eid->len;
2560 1264 : eid++;
2561 : }
2562 :
2563 : /* put something sane in the date fields */
2564 266 : ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
2565 266 : ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
2566 266 : ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
2567 266 : ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
2568 :
2569 266 : if (rc != 0) {
2570 0 : TALLOC_FREE(ad);
2571 : }
2572 266 : return ad;
2573 : }
2574 :
2575 34642 : static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
2576 : vfs_handle_struct *handle,
2577 : files_struct *fsp,
2578 : const struct smb_filename *smb_fname,
2579 : adouble_type_t type)
2580 : {
2581 34642 : int rc = 0;
2582 0 : ssize_t len;
2583 34642 : struct adouble *ad = NULL;
2584 0 : int mode;
2585 :
2586 34642 : if (fsp != NULL) {
2587 1476 : if (fsp_is_alternate_stream(fsp)) {
2588 274 : smb_fname = fsp->base_fsp->fsp_name;
2589 : } else {
2590 1202 : smb_fname = fsp->fsp_name;
2591 : }
2592 : }
2593 :
2594 34642 : DEBUG(10, ("ad_get(%s) called for %s\n",
2595 : type == ADOUBLE_META ? "meta" : "rsrc",
2596 : smb_fname != NULL ? smb_fname->base_name : "???"));
2597 :
2598 34642 : ad = ad_alloc(ctx, type);
2599 34642 : if (ad == NULL) {
2600 0 : rc = -1;
2601 0 : goto exit;
2602 : }
2603 :
2604 : /* Try rw first so we can use the fd in ad_convert() */
2605 34642 : mode = O_RDWR;
2606 :
2607 34642 : rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2608 34642 : if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
2609 0 : mode = O_RDONLY;
2610 0 : rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
2611 : }
2612 34642 : if (rc == -1) {
2613 7136 : DBG_DEBUG("ad_open [%s] error [%s]\n",
2614 : smb_fname->base_name, strerror(errno));
2615 7136 : goto exit;
2616 :
2617 : }
2618 :
2619 27506 : len = ad_read(handle, ad, smb_fname);
2620 27506 : if (len == -1) {
2621 24020 : DEBUG(10, ("error reading AppleDouble for %s\n",
2622 : smb_fname->base_name));
2623 24020 : rc = -1;
2624 24020 : goto exit;
2625 : }
2626 :
2627 3486 : exit:
2628 34642 : DEBUG(10, ("ad_get(%s) for %s returning %d\n",
2629 : type == ADOUBLE_META ? "meta" : "rsrc",
2630 : smb_fname->base_name, rc));
2631 :
2632 34642 : if (rc != 0) {
2633 31156 : TALLOC_FREE(ad);
2634 : }
2635 34642 : return ad;
2636 : }
2637 :
2638 : /**
2639 : * Return AppleDouble data for a file
2640 : *
2641 : * @param[in] ctx talloc context
2642 : * @param[in] handle vfs handle
2643 : * @param[in] smb_fname pathname to file or directory
2644 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2645 : *
2646 : * @return talloced struct adouble or NULL on error
2647 : **/
2648 33166 : struct adouble *ad_get(TALLOC_CTX *ctx,
2649 : vfs_handle_struct *handle,
2650 : const struct smb_filename *smb_fname,
2651 : adouble_type_t type)
2652 : {
2653 33166 : return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2654 : }
2655 :
2656 : /**
2657 : * Return AppleDouble data for a file
2658 : *
2659 : * @param[in] ctx talloc context
2660 : * @param[in] handle vfs handle
2661 : * @param[in] fsp fsp to use for IO
2662 : * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2663 : *
2664 : * @return talloced struct adouble or NULL on error
2665 : **/
2666 1476 : struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2667 : files_struct *fsp, adouble_type_t type)
2668 : {
2669 1476 : return ad_get_internal(ctx, handle, fsp, NULL, type);
2670 : }
2671 :
2672 : /**
2673 : * Set AppleDouble metadata on a file or directory
2674 : *
2675 : * @param[in] ad adouble handle
2676 : * @param[in] fsp file handle
2677 : *
2678 : * @return status code, 0 means success
2679 : **/
2680 492 : int ad_fset(struct vfs_handle_struct *handle,
2681 : struct adouble *ad,
2682 : files_struct *fsp)
2683 : {
2684 492 : int rc = -1;
2685 0 : ssize_t len;
2686 0 : bool ok;
2687 :
2688 492 : DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2689 :
2690 492 : ok = ad_pack(handle, ad, fsp);
2691 492 : if (!ok) {
2692 0 : return -1;
2693 : }
2694 :
2695 492 : switch (ad->ad_type) {
2696 136 : case ADOUBLE_META:
2697 136 : rc = SMB_VFS_NEXT_FSETXATTR(handle,
2698 : fsp->base_fsp ? fsp->base_fsp : fsp,
2699 : AFPINFO_EA_NETATALK,
2700 : ad->ad_data,
2701 : AD_DATASZ_XATTR, 0);
2702 136 : break;
2703 356 : case ADOUBLE_RSRC:
2704 356 : len = SMB_VFS_NEXT_PWRITE(handle,
2705 : fsp,
2706 : ad->ad_data,
2707 : ad_getentryoff(ad, ADEID_RFORK),
2708 : 0);
2709 356 : if (len != ad_getentryoff(ad, ADEID_RFORK)) {
2710 0 : DBG_ERR("short write on %s: %zd\n", fsp_str_dbg(fsp), len);
2711 0 : return -1;
2712 : }
2713 356 : rc = 0;
2714 356 : break;
2715 :
2716 0 : default:
2717 0 : return -1;
2718 : }
2719 :
2720 492 : DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2721 :
2722 492 : return rc;
2723 : }
2724 :
2725 722 : bool is_adouble_file(const char *path)
2726 : {
2727 722 : const char *p = NULL;
2728 0 : int match;
2729 :
2730 722 : p = strrchr(path, '/');
2731 722 : if (p == NULL) {
2732 722 : p = path;
2733 : } else {
2734 0 : p++;
2735 : }
2736 :
2737 722 : match = strncmp(p,
2738 : ADOUBLE_NAME_PREFIX,
2739 : strlen(ADOUBLE_NAME_PREFIX));
2740 722 : if (match != 0) {
2741 704 : return false;
2742 : }
2743 18 : return true;
2744 : }
2745 :
2746 : /**
2747 : * Prepend "._" to a basename
2748 : * Return a new struct smb_filename with stream_name == NULL.
2749 : **/
2750 9826 : int adouble_path(TALLOC_CTX *ctx,
2751 : const struct smb_filename *smb_fname_in,
2752 : struct smb_filename **pp_smb_fname_out)
2753 : {
2754 0 : char *parent;
2755 0 : const char *base;
2756 9826 : struct smb_filename *smb_fname = NULL;
2757 :
2758 9826 : smb_fname = cp_smb_filename_nostream(ctx, smb_fname_in);
2759 9826 : if (smb_fname == NULL) {
2760 0 : return -1;
2761 : }
2762 :
2763 : /* We're replacing base_name. */
2764 9826 : TALLOC_FREE(smb_fname->base_name);
2765 :
2766 9826 : SET_STAT_INVALID(smb_fname->st);
2767 :
2768 9826 : if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2769 : &parent, &base)) {
2770 0 : TALLOC_FREE(smb_fname);
2771 0 : return -1;
2772 : }
2773 :
2774 9826 : if (ISDOT(parent)) {
2775 5248 : smb_fname->base_name = talloc_asprintf(smb_fname,
2776 : "._%s", base);
2777 : } else {
2778 4578 : smb_fname->base_name = talloc_asprintf(smb_fname,
2779 : "%s/._%s", parent, base);
2780 : }
2781 9826 : if (smb_fname->base_name == NULL) {
2782 0 : TALLOC_FREE(smb_fname);
2783 0 : return -1;
2784 : }
2785 :
2786 9826 : *pp_smb_fname_out = smb_fname;
2787 :
2788 9826 : return 0;
2789 : }
2790 :
2791 : /**
2792 : * Allocate and initialize an AfpInfo struct
2793 : **/
2794 224 : AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2795 : {
2796 224 : AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2797 224 : if (ai == NULL) {
2798 0 : return NULL;
2799 : }
2800 224 : ai->afpi_Signature = AFP_Signature;
2801 224 : ai->afpi_Version = AFP_Version;
2802 224 : ai->afpi_BackupTime = AD_DATE_START;
2803 224 : return ai;
2804 : }
2805 :
2806 : /**
2807 : * Pack an AfpInfo struct into a buffer
2808 : *
2809 : * Buffer size must be at least AFP_INFO_SIZE
2810 : * Returns size of packed buffer
2811 : **/
2812 224 : ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2813 : {
2814 224 : memset(buf, 0, AFP_INFO_SIZE);
2815 :
2816 224 : RSIVAL(buf, 0, ai->afpi_Signature);
2817 224 : RSIVAL(buf, 4, ai->afpi_Version);
2818 224 : RSIVAL(buf, 12, ai->afpi_BackupTime);
2819 224 : memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2820 :
2821 224 : return AFP_INFO_SIZE;
2822 : }
2823 :
2824 : /**
2825 : * Unpack a buffer into a AfpInfo structure
2826 : *
2827 : * Buffer size must be at least AFP_INFO_SIZE
2828 : * Returns allocated AfpInfo struct
2829 : **/
2830 540 : AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate)
2831 : {
2832 540 : AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2833 540 : if (ai == NULL) {
2834 0 : return NULL;
2835 : }
2836 :
2837 540 : ai->afpi_Signature = RIVAL(data, 0);
2838 540 : ai->afpi_Version = RIVAL(data, 4);
2839 540 : ai->afpi_BackupTime = RIVAL(data, 12);
2840 540 : memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2841 : sizeof(ai->afpi_FinderInfo));
2842 :
2843 540 : if (validate) {
2844 536 : if (ai->afpi_Signature != AFP_Signature
2845 536 : || ai->afpi_Version != AFP_Version)
2846 : {
2847 0 : DEBUG(1, ("Bad AfpInfo signature or version\n"));
2848 0 : TALLOC_FREE(ai);
2849 : }
2850 : } else {
2851 4 : ai->afpi_Signature = AFP_Signature;
2852 4 : ai->afpi_Version = AFP_Version;
2853 : }
2854 :
2855 540 : return ai;
2856 : }
|