]> 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 #include <atalk/netatalk_conf.h>
32
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         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         adp = &ad;
154         ad_init(adp, vol);
155         if (ad_metadata(uname, adflags, adp) != 0 ) {
156             switch (errno) {
157             case ENOENT:
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         } else {
168             FinderInfo = ad_entry(adp, ADEID_FINDERI);
169
170             if ((adflags & ADFLAGS_DIR)) {
171                 /* set default view */
172                 uint16 = htons(FINDERINFO_CLOSEDVIEW);
173                 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
174             }
175
176             /* Check if FinderInfo equals default and empty FinderInfo*/
177             if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
178                 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
179                 strcpy(attrnamebuf, ea_finderinfo);
180                 attrbuflen += strlen(ea_finderinfo) + 1;
181                 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
182             }
183
184             /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
185             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen);
186
187             if (adp->ad_rlen > 0) {
188                 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
189                 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
190                 attrbuflen += strlen(ea_resourcefork) + 1;
191             }
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     } /* if ((maxreply == 0) || (buf_valid == 0)) */
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
237     if (adp)
238         ad_close(adp, ADFLAGS_HF);
239
240     return ret;
241 }
242
243 static char *to_stringz(char *ibuf, uint16_t len)
244 {
245 static char attrmname[256];
246
247     if (len > 255)
248         /* dont fool with us */
249         len = 255;
250
251     /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
252     strlcpy(attrmname, ibuf, len + 1);
253     return attrmname;
254 }
255
256 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
257 {
258     int                 ret, oflag = 0;
259     uint16_t            vid, bitmap, attrnamelen;
260     uint32_t            did, maxreply;
261     char                attruname[256];
262     struct vol          *vol;
263     struct dir          *dir;
264     struct path         *s_path;
265
266
267     *rbuflen = 0;
268     ibuf += 2;
269
270     memcpy( &vid, ibuf, sizeof(vid));
271     ibuf += sizeof(vid);
272     if (NULL == ( vol = getvolbyvid( vid )) ) {
273         LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
274         return AFPERR_ACCESS;
275     }
276
277     memcpy( &did, ibuf, sizeof(did));
278     ibuf += sizeof(did);
279     if (NULL == ( dir = dirlookup( vol, did )) ) {
280         LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
281         return afp_errno;
282     }
283
284     memcpy( &bitmap, ibuf, sizeof(bitmap));
285     bitmap = ntohs( bitmap );
286     ibuf += sizeof(bitmap);
287
288     if (bitmap & kXAttrNoFollow)
289         oflag = O_NOFOLLOW;
290
291     /* Skip Offset and ReqCount */
292     ibuf += 16;
293
294     /* Get MaxReply */
295     memcpy(&maxreply, ibuf, sizeof(maxreply));
296     maxreply = ntohl(maxreply);
297     ibuf += sizeof(maxreply);
298
299     /* get name */
300     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
301         LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
302         return AFPERR_NOOBJ;
303     }
304
305     if ((unsigned long)ibuf & 1)
306         ibuf++;
307
308     /* get length of EA name */
309     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
310     attrnamelen = ntohs(attrnamelen);
311     ibuf += sizeof(attrnamelen);
312
313     LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
314
315     /* Convert EA name in utf8 to unix charset */
316     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
317         return AFPERR_MISC;
318
319     /* write bitmap now */
320     bitmap = htons(bitmap);
321     memcpy(rbuf, &bitmap, sizeof(bitmap));
322     rbuf += sizeof(bitmap);
323     *rbuflen += sizeof(bitmap);
324
325     /*
326       Switch on maxreply:
327       if its 0 we must return the size of the requested attribute,
328       if its non 0 we must return the attribute.
329     */
330     if (maxreply == 0)
331         ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
332     else
333         ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
334
335     return ret;
336 }
337
338 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
339 {
340     int                 oflag = 0, ret;
341     uint16_t            vid, bitmap, attrnamelen;
342     uint32_t            did, attrsize;
343     char                attruname[256];
344     char                *attrmname;
345     struct vol          *vol;
346     struct dir          *dir;
347     struct path         *s_path;
348
349     *rbuflen = 0;
350     ibuf += 2;
351
352     memcpy( &vid, ibuf, sizeof(vid));
353     ibuf += sizeof(vid);
354     if (NULL == ( vol = getvolbyvid( vid )) ) {
355         LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
356         return AFPERR_ACCESS;
357     }
358
359     memcpy( &did, ibuf, sizeof(did));
360     ibuf += sizeof(did);
361     if (NULL == ( dir = dirlookup( vol, did )) ) {
362         LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
363         return afp_errno;
364     }
365
366     memcpy( &bitmap, ibuf, sizeof(bitmap));
367     bitmap = ntohs( bitmap );
368     ibuf += sizeof(bitmap);
369
370     if (bitmap & kXAttrNoFollow)
371         oflag |= O_NOFOLLOW;
372
373     if (bitmap & kXAttrCreate)
374         oflag |= O_CREAT;
375     else if (bitmap & kXAttrReplace)
376         oflag |= O_TRUNC;
377
378     /* Skip Offset */
379     ibuf += 8;
380
381     /* get name */
382     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
383         LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
384         return AFPERR_NOOBJ;
385     }
386
387     if ((unsigned long)ibuf & 1)
388         ibuf++;
389
390     /* get length of EA name */
391     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
392     attrnamelen = ntohs(attrnamelen);
393     ibuf += sizeof(attrnamelen);
394     if (attrnamelen > 255)
395         return AFPERR_PARAM;
396
397     attrmname = ibuf;
398     /* Convert EA name in utf8 to unix charset */
399     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
400         return AFPERR_MISC;
401
402     ibuf += attrnamelen;
403     /* get EA size */
404     memcpy(&attrsize, ibuf, sizeof(attrsize));
405     attrsize = ntohl(attrsize);
406     ibuf += sizeof(attrsize);
407     if (attrsize > MAX_EA_SIZE)
408         /* we arbitrarily make this fatal */
409         return AFPERR_PARAM;
410
411     LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
412
413     ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
414
415     return ret;
416 }
417
418 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
419 {
420     int                 oflag = 0, ret;
421     uint16_t            vid, bitmap, attrnamelen;
422     uint32_t            did;
423     char                attruname[256];
424     struct vol          *vol;
425     struct dir          *dir;
426     struct path         *s_path;
427
428     *rbuflen = 0;
429     ibuf += 2;
430
431     memcpy( &vid, ibuf, sizeof(vid));
432     ibuf += sizeof(vid);
433     if (NULL == ( vol = getvolbyvid( vid )) ) {
434         LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
435         return AFPERR_ACCESS;
436     }
437
438     memcpy( &did, ibuf, sizeof(did));
439     ibuf += sizeof(did);
440     if (NULL == ( dir = dirlookup( vol, did )) ) {
441         LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
442         return afp_errno;
443     }
444
445     memcpy( &bitmap, ibuf, sizeof(bitmap));
446     bitmap = ntohs( bitmap );
447     ibuf += sizeof(bitmap);
448
449     if (bitmap & kXAttrNoFollow)
450         oflag |= O_NOFOLLOW;
451
452     /* get name */
453     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
454         LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
455         return AFPERR_NOOBJ;
456     }
457
458     if ((unsigned long)ibuf & 1)
459         ibuf++;
460
461     /* get length of EA name */
462     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
463     attrnamelen = ntohs(attrnamelen);
464     ibuf += sizeof(attrnamelen);
465     if (attrnamelen > 255)
466         return AFPERR_PARAM;
467
468     /* Convert EA name in utf8 to unix charset */
469     if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
470         return AFPERR_MISC;
471
472     LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
473
474     ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);
475
476     return ret;
477 }
478