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