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