]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/extattrs.c
xattr, in AFP xattr request don't use log_error but log_debug if parameters are wrong.
[netatalk.git] / etc / afpd / extattrs.c
1 /*
2   $Id: extattrs.c,v 1.24 2009-11-13 13:27:12 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
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, *adp = NULL;
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         adp = of_ad(vol, s_path, &ad);
143         uname = s_path->u_name;        /*
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         if ( ad_metadata( uname, adflags, adp) < 0 ) {
154             switch (errno) {
155             case EACCES:
156                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
157                     uname, strerror(errno));
158                 return AFPERR_ACCESS;
159             default:
160                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
161                 return AFPERR_MISC;
162             }
163         }
164
165         FinderInfo = ad_entry(adp, ADEID_FINDERI);
166
167 #ifdef DEBUG
168         LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
169         hexdump( FinderInfo, 32);
170 #endif
171
172         if ((adflags & ADFLAGS_DIR)) {
173             /* set default view */
174             uint16 = htons(FINDERINFO_CLOSEDVIEW);
175             memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
176         }
177
178         /* Check if FinderInfo equals default and empty FinderInfo*/
179         if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
180             /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
181             strcpy(attrnamebuf, ea_finderinfo);
182             attrbuflen += strlen(ea_finderinfo) + 1;
183             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
184         }
185
186         /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
187         LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
188         if (adp->ad_eid[ADEID_RFORK].ade_len > 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         ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
195
196         switch (ret) {
197         case AFPERR_BADTYPE:
198             /* its a symlink and client requested O_NOFOLLOW */
199             LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
200             attrbuflen = 0;
201             buf_valid = 0;
202             ret = AFP_OK;
203             goto exit;
204         case AFPERR_MISC:
205             attrbuflen = 0;
206             goto exit;
207         default:
208             buf_valid = 1;
209         }
210     }
211
212     /* Start building reply packet */
213     bitmap = htons(bitmap);
214     memcpy( rbuf, &bitmap, sizeof(bitmap));
215     rbuf += sizeof(bitmap);
216     *rbuflen += sizeof(bitmap);
217
218     tmpattr = htonl(attrbuflen);
219     memcpy( rbuf, &tmpattr, sizeof(tmpattr));
220     rbuf += sizeof(tmpattr);
221     *rbuflen += sizeof(tmpattr);
222
223     /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
224        and we didnt have an error (buf_valid) */
225     if (maxreply && buf_valid) {
226         memcpy( rbuf, attrnamebuf, attrbuflen);
227         *rbuflen += attrbuflen;
228         buf_valid = 0;
229     }
230
231     ret = AFP_OK;
232
233 exit:
234     if (ret != AFP_OK)
235         buf_valid = 0;
236     if (adp)
237         ad_close_metadata( adp);
238
239     return ret;
240 }
241
242 static char *to_stringz(char *ibuf, uint16_t len)
243 {
244 static char attrmname[256];
245
246     if (len > 256)
247         /* dont fool with us */
248         len = 256;
249
250     /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
251     strlcpy(attrmname, ibuf, len);
252     return attrmname;
253 }
254
255 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
256 {
257     int                 ret, oflag = 0;
258     uint16_t            vid, bitmap, attrnamelen;
259     uint32_t            did, maxreply;
260     char                attruname[256];
261     struct vol          *vol;
262     struct dir          *dir;
263     struct path         *s_path;
264
265
266     *rbuflen = 0;
267     ibuf += 2;
268
269     memcpy( &vid, ibuf, sizeof(vid));
270     ibuf += sizeof(vid);
271     if (NULL == ( vol = getvolbyvid( vid )) ) {
272         LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
273         return AFPERR_ACCESS;
274     }
275
276     memcpy( &did, ibuf, sizeof(did));
277     ibuf += sizeof(did);
278     if (NULL == ( dir = dirlookup( vol, did )) ) {
279         LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
280         return afp_errno;
281     }
282
283     memcpy( &bitmap, ibuf, sizeof(bitmap));
284     bitmap = ntohs( bitmap );
285     ibuf += sizeof(bitmap);
286
287     if (bitmap & kXAttrNoFollow)
288         oflag = O_NOFOLLOW;
289
290     /* Skip Offset and ReqCount */
291     ibuf += 16;
292
293     /* Get MaxReply */
294     memcpy(&maxreply, ibuf, sizeof(maxreply));
295     maxreply = ntohl(maxreply);
296     ibuf += sizeof(maxreply);
297
298     /* get name */
299     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
300         LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
301         return AFPERR_NOOBJ;
302     }
303
304     if ((unsigned long)ibuf & 1)
305         ibuf++;
306
307     /* get length of EA name */
308     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
309     attrnamelen = ntohs(attrnamelen);
310     ibuf += sizeof(attrnamelen);
311
312     LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
313
314     /* Convert EA name in utf8 to unix charset */
315     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
316         return AFPERR_MISC;
317
318     /* write bitmap now */
319     bitmap = htons(bitmap);
320     memcpy(rbuf, &bitmap, sizeof(bitmap));
321     rbuf += sizeof(bitmap);
322     *rbuflen += sizeof(bitmap);
323
324     /*
325       Switch on maxreply:
326       if its 0 we must return the size of the requested attribute,
327       if its non 0 we must return the attribute.
328     */
329     if (maxreply == 0)
330         ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
331     else
332         ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
333
334     return ret;
335 }
336
337 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
338 {
339     int                 oflag = 0, ret;
340     uint16_t            vid, bitmap, attrnamelen;
341     uint32_t            did, attrsize;
342     char                attruname[256];
343     char                *attrmname;
344     struct vol          *vol;
345     struct dir          *dir;
346     struct path         *s_path;
347
348     *rbuflen = 0;
349     ibuf += 2;
350
351     memcpy( &vid, ibuf, sizeof(vid));
352     ibuf += sizeof(vid);
353     if (NULL == ( vol = getvolbyvid( vid )) ) {
354         LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
355         return AFPERR_ACCESS;
356     }
357
358     memcpy( &did, ibuf, sizeof(did));
359     ibuf += sizeof(did);
360     if (NULL == ( dir = dirlookup( vol, did )) ) {
361         LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
362         return afp_errno;
363     }
364
365     memcpy( &bitmap, ibuf, sizeof(bitmap));
366     bitmap = ntohs( bitmap );
367     ibuf += sizeof(bitmap);
368
369     if (bitmap & kXAttrNoFollow)
370         oflag |= O_NOFOLLOW;
371
372     if (bitmap & kXAttrCreate)
373         oflag |= O_CREAT;
374     else if (bitmap & kXAttrReplace)
375         oflag |= O_TRUNC;
376
377     /* Skip Offset */
378     ibuf += 8;
379
380     /* get name */
381     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
382         LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
383         return AFPERR_NOOBJ;
384     }
385
386     if ((unsigned long)ibuf & 1)
387         ibuf++;
388
389     /* get length of EA name */
390     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
391     attrnamelen = ntohs(attrnamelen);
392     ibuf += sizeof(attrnamelen);
393     if (attrnamelen > 255)
394         return AFPERR_PARAM;
395
396     attrmname = ibuf;
397     /* Convert EA name in utf8 to unix charset */
398     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
399         return AFPERR_MISC;
400
401     ibuf += attrnamelen;
402     /* get EA size */
403     memcpy(&attrsize, ibuf, sizeof(attrsize));
404     attrsize = ntohl(attrsize);
405     ibuf += sizeof(attrsize);
406     if (attrsize > MAX_EA_SIZE)
407         /* we arbitrarily make this fatal */
408         return AFPERR_PARAM;
409
410     LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
411
412     ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
413
414     return ret;
415 }
416
417 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
418 {
419     int                 oflag = 0, ret;
420     uint16_t            vid, bitmap, attrnamelen;
421     uint32_t            did;
422     char                attruname[256];
423     struct vol          *vol;
424     struct dir          *dir;
425     struct path         *s_path;
426
427     *rbuflen = 0;
428     ibuf += 2;
429
430     memcpy( &vid, ibuf, sizeof(vid));
431     ibuf += sizeof(vid);
432     if (NULL == ( vol = getvolbyvid( vid )) ) {
433         LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
434         return AFPERR_ACCESS;
435     }
436
437     memcpy( &did, ibuf, sizeof(did));
438     ibuf += sizeof(did);
439     if (NULL == ( dir = dirlookup( vol, did )) ) {
440         LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
441         return afp_errno;
442     }
443
444     memcpy( &bitmap, ibuf, sizeof(bitmap));
445     bitmap = ntohs( bitmap );
446     ibuf += sizeof(bitmap);
447
448     if (bitmap & kXAttrNoFollow)
449         oflag |= O_NOFOLLOW;
450
451     /* get name */
452     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
453         LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
454         return AFPERR_NOOBJ;
455     }
456
457     if ((unsigned long)ibuf & 1)
458         ibuf++;
459
460     /* get length of EA name */
461     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
462     attrnamelen = ntohs(attrnamelen);
463     ibuf += sizeof(attrnamelen);
464     if (attrnamelen > 255)
465         return AFPERR_PARAM;
466
467     /* Convert EA name in utf8 to unix charset */
468     if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
469         return AFPERR_MISC;
470
471     LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
472
473     ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);
474
475     return ret;
476 }
477