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