]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/extattrs.c
Merge master
[netatalk.git] / etc / afpd / extattrs.c
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
32 #include "volume.h"
33 #include "desktop.h"
34 #include "directory.h"
35 #include "fork.h"
36 #include "extattrs.h"
37
38 static const char *ea_finderinfo = "com.apple.FinderInfo";
39 static const char *ea_resourcefork = "com.apple.ResourceFork";
40
41 /* This should be big enough to consecutively store the names of all attributes */
42 static char attrnamebuf[ATTRNAMEBUFSIZ];
43
44 #ifdef DEBUG
45 static void hexdump(void *m, size_t l) {
46     char *p = m;
47     int count = 0, len;
48     char buf[100];
49     char *bufp = buf;
50
51     while (l--) {
52         len = sprintf(bufp, "%02x ", *p++);
53         bufp += len;
54         count++;
55
56         if ((count % 16) == 0) {
57             LOG(log_debug9, logtype_afpd, "%s", buf);
58             bufp = buf;
59         }
60     }
61 }
62 #endif
63
64 /***************************************
65  * AFP funcs
66  ****************************************/
67
68 /*
69   Note: we're being called twice. Firstly the client only want the size of all
70   EA names, secondly it wants these names. In order to avoid scanning EAs twice
71   we cache them in a static buffer.
72 */
73 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
74 {
75     int                 ret, oflag = 0, adflags = 0;
76     uint16_t            vid, bitmap, uint16;
77     uint32_t            did, maxreply, tmpattr;
78     struct vol          *vol;
79     struct dir          *dir;
80     struct path         *s_path;
81     struct stat         *st;
82     struct adouble      ad, *adp = NULL;
83     char                *uname, *FinderInfo;
84     char                emptyFinderInfo[32] = { 0 };
85
86     static int          buf_valid = 0;
87     static size_t       attrbuflen = 0;
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     /*
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, sizeof(vid));
108         ibuf += sizeof(vid);
109         if (NULL == ( vol = getvolbyvid( vid )) ) {
110             LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
111             return AFPERR_ACCESS;
112         }
113
114         memcpy( &did, ibuf, sizeof(did));
115         ibuf += sizeof(did);
116         if (NULL == ( dir = dirlookup( vol, did )) ) {
117             LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
118             return afp_errno;
119         }
120
121         if (bitmap & kXAttrNoFollow)
122             oflag = O_NOFOLLOW;
123         /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
124         ibuf += 12;
125
126         /* get name */
127         if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
128             LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
129             return AFPERR_NOOBJ;
130         }
131
132         st   = &s_path->st;
133         if (!s_path->st_valid) {
134             /* it's a dir in our cache, we didn't stat it, do it now */
135             of_statdir(vol, s_path);
136         }
137         if ( s_path->st_errno != 0 ) {
138             return( AFPERR_NOOBJ );
139         }
140
141         uname = s_path->u_name;
142         /*
143           We have to check the FinderInfo for the file, because if they aren't all 0
144           we must return the synthetic attribute "com.apple.FinderInfo".
145           Note: the client will never (never seen in traces) request that attribute
146           via FPGetExtAttr !
147         */
148
149         if (S_ISDIR(st->st_mode))
150             adflags = ADFLAGS_DIR;
151
152         adp = &ad;
153         ad_init(adp, vol->v_adouble, vol->v_ad_options);
154         if (ad_metadata(uname, adflags, adp) != 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(adp, 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, adp->ad_rlen);
185
186             if (adp->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     } /* if ((maxreply == 0) || (buf_valid == 0)) */
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     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 > 255)
247         /* dont fool with us */
248         len = 255;
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 + 1);
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