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