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