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