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