1 /*
2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <atalk/adouble.h>
25 #include <atalk/util.h>
26 #include <atalk/vfs.h>
27 #include <atalk/afp.h>
28 #include <atalk/logger.h>
29 #include <atalk/ea.h>
30 #include <atalk/globals.h>
31 #include <atalk/netatalk_conf.h>
32
33 #include "volume.h"
34 #include "desktop.h"
35 #include "directory.h"
36 #include "fork.h"
37 #include "extattrs.h"
38
39 static const char *ea_finderinfo = "com.apple.FinderInfo";
40 static const char *ea_resourcefork = "com.apple.ResourceFork";
41
42
43 #ifdef DEBUG
hexdump(void * m,size_t l)44 static void hexdump(void *m, size_t l) {
45 char *p = m;
46 int count = 0, len;
47 char buf[100];
48 char *bufp = buf;
49
50 while (l--) {
51 len = sprintf(bufp, "%02x ", *p++);
52 bufp += len;
53 count++;
54
55 if ((count % 16) == 0) {
56 LOG(log_debug9, logtype_afpd, "%s", buf);
57 bufp = buf;
58 }
59 }
60 }
61 #endif
62
63 /***************************************
64 * AFP funcs
65 ****************************************/
66
67 /*
68 Note: we're being called twice. Firstly the client only want the size of all
69 EA names, secondly it wants these names. In order to avoid scanning EAs twice
70 we cache them in a static buffer.
71 */
afp_listextattr(AFPObj * obj _U_,char * ibuf,size_t ibuflen _U_,char * rbuf,size_t * rbuflen)72 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
73 {
74 int ret, oflag = 0, adflags = 0, fd = -1;
75 uint16_t vid, bitmap, uint16;
76 uint32_t did, maxreply, tmpattr;
77 struct vol *vol;
78 struct dir *dir;
79 struct path *s_path;
80 struct stat *st;
81 struct adouble ad, *adp = NULL;
82 struct ofork *opened = NULL;
83 char *uname, *FinderInfo;
84 char emptyFinderInfo[32] = { 0 };
85 size_t attrbuflen = 0;
86 bool close_ad = false;
87 char attrnamebuf[ATTRNAMEBUFSIZ];
88
89 *rbuflen = 0;
90 ibuf += 2;
91
92 /* Get Bitmap and MaxReplySize first */
93 memcpy(&bitmap, ibuf +6, sizeof(bitmap));
94 bitmap = ntohs(bitmap);
95
96 memcpy(&maxreply, ibuf + 14, sizeof (maxreply));
97 maxreply = ntohl(maxreply);
98
99 memcpy(&vid, ibuf, sizeof(vid));
100 ibuf += sizeof(vid);
101 vol = getvolbyvid(vid);
102 if (vol == NULL) {
103 LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
104 return AFPERR_ACCESS;
105 }
106
107 memcpy(&did, ibuf, sizeof(did));
108 ibuf += sizeof(did);
109 dir = dirlookup(vol, did);
110 if (dir == NULL) {
111 LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
112 return afp_errno;
113 }
114
115 if (bitmap & kXAttrNoFollow) {
116 oflag = O_NOFOLLOW;
117 }
118
119 /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
120 ibuf += 12;
121
122 /* get name */
123 s_path = cname(vol, dir, &ibuf);
124 if (s_path == NULL) {
125 LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
126 return AFPERR_NOOBJ;
127 }
128
129 st = &s_path->st;
130 if (!s_path->st_valid) {
131 /* it's a dir in our cache, we didn't stat it, do it now */
132 of_statdir(vol, s_path);
133 }
134 if (s_path->st_errno != 0) {
135 return(AFPERR_NOOBJ);
136 }
137
138 uname = s_path->u_name;
139
140 /*
141 * We have to check the FinderInfo for the file, because if they
142 * aren't all 0 we must return the synthetic attribute
143 * "com.apple.FinderInfo". Note: the client will never (never
144 * seen in traces) request that attribute via FPGetExtAttr !
145 */
146
147 adp = &ad;
148 ad_init(adp, vol);
149
150 if (path_isadir(s_path)) {
151 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): is a dir", uname);
152 adflags = ADFLAGS_DIR;
153 } else {
154 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): is a file", uname);
155 opened = of_findname(vol, s_path);
156 if (opened) {
157 adp = opened->of_ad;
158 fd = ad_meta_fileno(adp);
159 }
160 }
161
162 if (ad_metadata(uname, adflags, adp) != 0 ) {
163 switch (errno) {
164 case ENOENT:
165 break;
166 case EACCES:
167 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
168 uname, strerror(errno));
169 return AFPERR_ACCESS;
170 default:
171 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
172 return AFPERR_MISC;
173 }
174 } else {
175 close_ad = true;
176 FinderInfo = ad_entry(adp, ADEID_FINDERI);
177 /* Check if FinderInfo equals default and empty FinderInfo*/
178 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
179 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
180 strcpy(attrnamebuf, ea_finderinfo);
181 attrbuflen += strlen(ea_finderinfo) + 1;
182 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
183 }
184
185 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
186 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen);
187
188 if (adp->ad_rlen > 0) {
189 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
190 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
191 attrbuflen += strlen(ea_resourcefork) + 1;
192 }
193 }
194
195 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag, fd);
196 if (ret != AFP_OK) {
197 attrbuflen = 0;
198
199 switch (ret) {
200 case AFPERR_BADTYPE:
201 /* its a symlink and client requested O_NOFOLLOW */
202 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
203 ret = AFP_OK;
204 break;
205
206 default:
207 break;
208 }
209 }
210
211 exit:
212 bitmap = htons(bitmap);
213 memcpy( rbuf, &bitmap, sizeof(bitmap));
214 rbuf += sizeof(bitmap);
215 *rbuflen += sizeof(bitmap);
216
217 tmpattr = htonl(attrbuflen);
218 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
219 rbuf += sizeof(tmpattr);
220 *rbuflen += sizeof(tmpattr);
221
222 if (maxreply > 0) {
223 memcpy( rbuf, attrnamebuf, attrbuflen);
224 *rbuflen += attrbuflen;
225 }
226
227 if (close_ad) {
228 ad_close(adp, ADFLAGS_HF);
229 }
230
231 return ret;
232 }
233
to_stringz(char * ibuf,uint16_t len)234 static char *to_stringz(char *ibuf, uint16_t len)
235 {
236 static char attrmname[256];
237
238 if (len > 255)
239 /* don't fool with us */
240 len = 255;
241
242 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
243 strlcpy(attrmname, ibuf, len + 1);
244 return attrmname;
245 }
246
afp_getextattr(AFPObj * obj _U_,char * ibuf,size_t ibuflen _U_,char * rbuf,size_t * rbuflen)247 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
248 {
249 int ret, oflag = 0, fd = -1;
250 uint16_t vid, bitmap, attrnamelen;
251 uint32_t did, maxreply;
252 char attruname[256];
253 struct vol *vol;
254 struct dir *dir;
255 struct path *s_path;
256 struct adouble ad, *adp = NULL;
257 struct ofork *opened = NULL;
258
259
260 *rbuflen = 0;
261 ibuf += 2;
262
263 memcpy( &vid, ibuf, sizeof(vid));
264 ibuf += sizeof(vid);
265 if (NULL == ( vol = getvolbyvid( vid )) ) {
266 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
267 return AFPERR_ACCESS;
268 }
269
270 memcpy( &did, ibuf, sizeof(did));
271 ibuf += sizeof(did);
272 if (NULL == ( dir = dirlookup( vol, did )) ) {
273 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
274 return afp_errno;
275 }
276
277 memcpy( &bitmap, ibuf, sizeof(bitmap));
278 bitmap = ntohs( bitmap );
279 ibuf += sizeof(bitmap);
280
281 if (bitmap & kXAttrNoFollow)
282 oflag = O_NOFOLLOW;
283
284 /* Skip Offset and ReqCount */
285 ibuf += 16;
286
287 /* Get MaxReply */
288 memcpy(&maxreply, ibuf, sizeof(maxreply));
289 maxreply = ntohl(maxreply);
290 ibuf += sizeof(maxreply);
291
292 /* get name */
293 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
294 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
295 return AFPERR_NOOBJ;
296 }
297
298 if ((unsigned long)ibuf & 1)
299 ibuf++;
300
301 /* get length of EA name */
302 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
303 attrnamelen = ntohs(attrnamelen);
304 ibuf += sizeof(attrnamelen);
305
306 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
307
308 /* Convert EA name in utf8 to unix charset */
309 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
310 return AFPERR_MISC;
311
312 /* write bitmap now */
313 bitmap = htons(bitmap);
314 memcpy(rbuf, &bitmap, sizeof(bitmap));
315 rbuf += sizeof(bitmap);
316 *rbuflen += sizeof(bitmap);
317
318 if (path_isadir(s_path)) {
319 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): is a dir", s_path->u_name);
320 } else {
321 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): is a file", s_path->u_name);
322 opened = of_findname(vol, s_path);
323 if (opened) {
324 adp = opened->of_ad;
325 fd = ad_meta_fileno(adp);
326 }
327 }
328
329 /*
330 Switch on maxreply:
331 if its 0 we must return the size of the requested attribute,
332 if its non 0 we must return the attribute.
333 */
334 if (maxreply == 0)
335 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, fd);
336 else
337 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply, fd);
338
339 return ret;
340 }
341
afp_setextattr(AFPObj * obj _U_,char * ibuf,size_t ibuflen _U_,char * rbuf _U_,size_t * rbuflen)342 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
343 {
344 int oflag = 0, ret, fd = -1;
345 uint16_t vid, bitmap, attrnamelen;
346 uint32_t did, attrsize;
347 char attruname[256];
348 char *attrmname;
349 struct vol *vol;
350 struct dir *dir;
351 struct path *s_path;
352 struct adouble ad, *adp = NULL;
353 struct ofork *opened = NULL;
354
355 *rbuflen = 0;
356 ibuf += 2;
357
358 memcpy( &vid, ibuf, sizeof(vid));
359 ibuf += sizeof(vid);
360 if (NULL == ( vol = getvolbyvid( vid )) ) {
361 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
362 return AFPERR_ACCESS;
363 }
364
365 memcpy( &did, ibuf, sizeof(did));
366 ibuf += sizeof(did);
367 if (NULL == ( dir = dirlookup( vol, did )) ) {
368 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
369 return afp_errno;
370 }
371
372 memcpy( &bitmap, ibuf, sizeof(bitmap));
373 bitmap = ntohs( bitmap );
374 ibuf += sizeof(bitmap);
375
376 if (bitmap & kXAttrNoFollow)
377 oflag |= O_NOFOLLOW;
378
379 if (bitmap & kXAttrCreate)
380 oflag |= O_CREAT;
381 else if (bitmap & kXAttrReplace)
382 oflag |= O_TRUNC;
383
384 /* Skip Offset */
385 ibuf += 8;
386
387 /* get name */
388 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
389 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
390 return AFPERR_NOOBJ;
391 }
392
393 if (path_isadir(s_path)) {
394 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): is a dir", s_path->u_name);
395 } else {
396 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): is a file", s_path->u_name);
397 opened = of_findname(vol, s_path);
398 if (opened) {
399 adp = opened->of_ad;
400 fd = ad_meta_fileno(adp);
401 }
402 }
403
404
405 if ((unsigned long)ibuf & 1)
406 ibuf++;
407
408 /* get length of EA name */
409 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
410 attrnamelen = ntohs(attrnamelen);
411 ibuf += sizeof(attrnamelen);
412 if (attrnamelen > 255)
413 return AFPERR_PARAM;
414
415 attrmname = ibuf;
416 /* Convert EA name in utf8 to unix charset */
417 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
418 return AFPERR_MISC;
419
420 ibuf += attrnamelen;
421 /* get EA size */
422 memcpy(&attrsize, ibuf, sizeof(attrsize));
423 attrsize = ntohl(attrsize);
424 ibuf += sizeof(attrsize);
425 if (attrsize > MAX_EA_SIZE)
426 /* we arbitrarily make this fatal */
427 return AFPERR_PARAM;
428
429 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
430
431 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag, fd);
432
433 return ret;
434 }
435
afp_remextattr(AFPObj * obj _U_,char * ibuf,size_t ibuflen _U_,char * rbuf _U_,size_t * rbuflen)436 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
437 {
438 int oflag = 0, ret, fd = -1;
439 uint16_t vid, bitmap, attrnamelen;
440 uint32_t did;
441 char attruname[256];
442 struct vol *vol;
443 struct dir *dir;
444 struct path *s_path;
445 struct adouble ad, *adp = NULL;
446 struct ofork *opened = NULL;
447
448 *rbuflen = 0;
449 ibuf += 2;
450
451 memcpy( &vid, ibuf, sizeof(vid));
452 ibuf += sizeof(vid);
453 if (NULL == ( vol = getvolbyvid( vid )) ) {
454 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
455 return AFPERR_ACCESS;
456 }
457
458 memcpy( &did, ibuf, sizeof(did));
459 ibuf += sizeof(did);
460 if (NULL == ( dir = dirlookup( vol, did )) ) {
461 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
462 return afp_errno;
463 }
464
465 memcpy( &bitmap, ibuf, sizeof(bitmap));
466 bitmap = ntohs( bitmap );
467 ibuf += sizeof(bitmap);
468
469 if (bitmap & kXAttrNoFollow)
470 oflag |= O_NOFOLLOW;
471
472 /* get name */
473 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
474 LOG(log_debug, logtype_afpd, "afp_remextattr: cname error: %s", strerror(errno));
475 return AFPERR_NOOBJ;
476 }
477
478 if (path_isadir(s_path)) {
479 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): is a dir", s_path->u_name);
480 } else {
481 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): is a file", s_path->u_name);
482 opened = of_findname(vol, s_path);
483 if (opened) {
484 adp = opened->of_ad;
485 fd = ad_meta_fileno(adp);
486 }
487 }
488
489 if ((unsigned long)ibuf & 1)
490 ibuf++;
491
492 /* get length of EA name */
493 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
494 attrnamelen = ntohs(attrnamelen);
495 ibuf += sizeof(attrnamelen);
496 if (attrnamelen > 255)
497 return AFPERR_PARAM;
498
499 /* Convert EA name in utf8 to unix charset */
500 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
501 return AFPERR_MISC;
502
503 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
504
505 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag, fd);
506
507 return ret;
508 }
509
510