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