]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/extattrs.c
Default and empty FinderInfo for dirs are not all 0 like files are
[netatalk.git] / etc / afpd / extattrs.c
1 /*
2   $Id: extattrs.c,v 1.8 2009-10-20 09:53:42 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 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <dirent.h>
28
29 #include <atalk/adouble.h>
30 #include <atalk/vfs.h>
31 #include <atalk/afp.h>
32 #include <atalk/logger.h>
33 #include <atalk/ea.h>
34
35 #include "globals.h"
36 #include "volume.h"
37 #include "desktop.h"
38 #include "directory.h"
39 #include "fork.h"
40 #include "extattrs.h"
41
42 static char *ea_finderinfo = "com.apple.FinderInfo";
43 static char *ea_resourcefork = "com.apple.ResourceFork";
44
45 /* This should be big enough to consecutively store the names of all attributes */
46 static char attrnamebuf[ATTRNAMEBUFSIZ];
47
48 static void hexdump(void *m, size_t l) {
49     char *p = m;
50     int count = 0, len;
51     char buf[100];
52     char *bufp = buf;
53
54     while (l--) {
55         len = sprintf(bufp, "%02x ", *p++);
56         bufp += len;
57         count++;
58
59         if ((count % 16) == 0) {
60             LOG(log_debug9, logtype_afpd, "%s", buf);
61             bufp = buf;
62         }
63     }
64 }
65
66 /***************************************
67  * AFP funcs
68  ****************************************/
69
70 /*
71   Note: we're being called twice. Firstly the client only want the size of all
72   EA names, secondly it wants these names. In order to avoid scanning EAs twice
73   we cache them in a static buffer.
74 */
75 int afp_listextattr(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
76 {
77     int                 ret, oflag = 0, adflags = 0;
78     uint16_t            vid, bitmap, uint16;
79     uint32_t            did, maxreply, tmpattr;
80     struct vol          *vol;
81     struct dir          *dir;
82     struct path         *s_path;
83     struct stat         st;
84     struct adouble      ad, *adp = NULL;
85     struct ofork        *of;
86     char                *uname, *FinderInfo;
87     static int          buf_valid = 0, attrbuflen = 0;
88     char                emptyFinderInfo[32] = { 0 };
89
90     *rbuflen = 0;
91     ibuf += 2;
92
93     /* Get MaxReplySize first */
94     memcpy( &maxreply, ibuf + 14, 4);
95     maxreply = ntohl( maxreply );
96
97     /*
98       If its the first request with maxreply=0 or if we didn't mark our buffers valid for
99       whatever reason (just a safety check, it should be valid), then scan for attributes
100     */
101     if ((maxreply == 0) || (buf_valid == 0)) {
102
103         attrbuflen = 0;
104
105         memcpy( &vid, ibuf, 2);
106         ibuf += 2;
107         if (NULL == ( vol = getvolbyvid( vid )) ) {
108             LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
109             return AFPERR_ACCESS;
110         }
111
112         memcpy( &did, ibuf, 4);
113         ibuf += 4;
114         if (NULL == ( dir = dirlookup( vol, did )) ) {
115             LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
116             return afp_errno;
117         }
118
119         memcpy( &bitmap, ibuf, 2);
120         bitmap = ntohs( bitmap );
121         ibuf += 2;
122
123 #ifdef HAVE_SOLARIS_EAS
124         if (bitmap & kXAttrNoFollow)
125             oflag = O_NOFOLLOW;
126 #endif
127         /* Skip ReqCount, StartIndex and maxreply*/
128         ibuf += 10;
129
130         /* get name */
131         if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
132             LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
133             return AFPERR_NOOBJ;
134         }
135         uname = s_path->u_name;
136
137         /*
138           We have to check the FinderInfo for the file, because if they aren't all 0
139           we must return the synthetic attribute "com.apple.FinderInfo".
140           Note: the client will never (never seen in traces) request that attribute
141           via FPGetExtAttr !
142         */
143         if ((of = of_findname(s_path))) {
144             adp = of->of_ad;
145         } else {
146             ad_init(&ad, vol->v_adouble, vol->v_ad_options);
147             adp = &ad;
148         }
149
150         stat(uname, &st);
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 EACCES:
157                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
158                     uname, strerror(errno));
159                 return AFPERR_ACCESS;
160             default:
161                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
162                 return AFPERR_MISC;
163             }
164         }
165
166         FinderInfo = ad_entry(adp, ADEID_FINDERI);
167
168 #ifdef DEBUG
169         LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
170         hexdump( FinderInfo, 32);
171 #endif
172
173         if ((adflags & ADFLAGS_DIR)) {
174             /* set default view */
175             uint16 = htons(FINDERINFO_CLOSEDVIEW);
176             memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
177         }
178
179         /* Check if FinderInfo equals default and empty FinderInfo*/
180         if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
181             /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
182             strcpy(attrnamebuf, ea_finderinfo);
183             attrbuflen += strlen(ea_finderinfo) + 1;
184             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
185         }
186
187         /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
188         LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
189         if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
190             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
191             strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
192             attrbuflen += strlen(ea_resourcefork) + 1;
193         }
194
195         ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
196
197         switch (ret) {
198         case AFPERR_BADTYPE:
199             /* its a symlink and client requested O_NOFOLLOW */
200             LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
201             attrbuflen = 0;
202             buf_valid = 0;
203             ret = AFP_OK;
204             goto exit;
205         case AFPERR_MISC:
206             attrbuflen = 0;
207             goto exit;
208         default:
209             buf_valid = 1;
210         }
211     }
212
213     /* Start building reply packet */
214     bitmap = htons(bitmap);
215     memcpy( rbuf, &bitmap, 2);
216     rbuf += 2;
217     *rbuflen += 2;
218
219     tmpattr = htonl(attrbuflen);
220     memcpy( rbuf, &tmpattr, 4);
221     rbuf += 4;
222     *rbuflen += 4;
223
224     /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
225        and we didnt have an error (buf_valid) */
226     if (maxreply && buf_valid) {
227         memcpy( rbuf, attrnamebuf, attrbuflen);
228         *rbuflen += attrbuflen;
229         buf_valid = 0;
230     }
231
232     ret = AFP_OK;
233
234 exit:
235     if (ret != AFP_OK)
236         buf_valid = 0;
237     if (adp)
238         ad_close_metadata( adp);
239
240     return ret;
241 }
242
243 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
244 {
245     int                 ret, oflag = 0;
246     uint16_t            vid, bitmap;
247     uint32_t            did, maxreply, attrnamelen;
248     char                attrmname[256], attruname[256];
249     struct vol          *vol;
250     struct dir          *dir;
251     struct path         *s_path;
252
253
254     *rbuflen = 0;
255     ibuf += 2;
256
257     memcpy( &vid, ibuf, 2);
258     ibuf += 2;
259     if (NULL == ( vol = getvolbyvid( vid )) ) {
260         LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
261         return AFPERR_ACCESS;
262     }
263
264     memcpy( &did, ibuf, 4);
265     ibuf += 4;
266     if (NULL == ( dir = dirlookup( vol, did )) ) {
267         LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
268         return afp_errno;
269     }
270
271     memcpy( &bitmap, ibuf, 2);
272     bitmap = ntohs( bitmap );
273     ibuf += 2;
274
275 #ifdef HAVE_SOLARIS_EAS
276     if (bitmap & kXAttrNoFollow)
277         oflag = O_NOFOLLOW;
278 #endif
279
280     /* Skip Offset and ReqCount */
281     ibuf += 16;
282
283     /* Get MaxReply */
284     memcpy(&maxreply, ibuf, 4);
285     maxreply = ntohl(maxreply);
286     ibuf += 4;
287
288     /* get name */
289     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
290         LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
291         return AFPERR_NOOBJ;
292     }
293
294     if ((unsigned long)ibuf & 1)
295         ibuf++;
296
297     /* get length of EA name */
298     memcpy(&attrnamelen, ibuf, 2);
299     attrnamelen = ntohs(attrnamelen);
300     ibuf += 2;
301     if (attrnamelen > 255)
302         /* dont fool with us */
303         attrnamelen = 255;
304
305     /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
306     strncpy(attrmname, ibuf, attrnamelen);
307     attrmname[attrnamelen] = 0;
308
309     LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
310
311     /* Convert EA name in utf8 to unix charset */
312     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
313         return AFPERR_MISC;
314
315     if (attrnamelen == 255)
316         /* convert_string didn't 0-terminate */
317         attruname[255] = 0;
318
319     /* write bitmap now */
320     bitmap = htons(bitmap);
321     memcpy(rbuf, &bitmap, 2);
322     rbuf += 2;
323     *rbuflen += 2;
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, size_t *rbuflen)
339 {
340     int                 oflag = O_CREAT | O_WRONLY, ret;
341     uint16_t            vid, bitmap;
342     uint32_t            did, attrnamelen, attrsize;
343     char                attrmname[256], attruname[256];
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, 2);
352     ibuf += 2;
353     if (NULL == ( vol = getvolbyvid( vid )) ) {
354         LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
355         return AFPERR_ACCESS;
356     }
357
358     memcpy( &did, ibuf, 4);
359     ibuf += 4;
360     if (NULL == ( dir = dirlookup( vol, did )) ) {
361         LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
362         return afp_errno;
363     }
364
365     memcpy( &bitmap, ibuf, 2);
366     bitmap = ntohs( bitmap );
367     ibuf += 2;
368
369 #ifdef HAVE_SOLARIS_EAS
370     if (bitmap & kXAttrNoFollow)
371         oflag |= AT_SYMLINK_NOFOLLOW;
372 #endif
373
374     if (bitmap & kXAttrCreate)
375         oflag |= O_EXCL;
376     else if (bitmap & kXAttrReplace)
377         oflag |= O_TRUNC;
378
379     /* Skip Offset */
380     ibuf += 8;
381
382     /* get name */
383     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
384         LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
385         return AFPERR_NOOBJ;
386     }
387
388     if ((unsigned long)ibuf & 1)
389         ibuf++;
390
391     /* get length of EA name */
392     memcpy(&attrnamelen, ibuf, 2);
393     attrnamelen = ntohs(attrnamelen);
394     ibuf += 2;
395     if (attrnamelen > 255)
396         return AFPERR_PARAM;
397
398     /* we must copy the name as its not 0-terminated and we cant write to ibuf */
399     strncpy(attrmname, ibuf, attrnamelen);
400     attrmname[attrnamelen] = 0;
401     ibuf += attrnamelen;
402
403     /* Convert EA name in utf8 to unix charset */
404     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
405         return AFPERR_MISC;
406
407     if (attrnamelen == 255)
408         /* convert_string didn't 0-terminate */
409         attruname[255] = 0;
410
411     /* get EA size */
412     memcpy(&attrsize, ibuf, 4);
413     attrsize = ntohl(attrsize);
414     ibuf += 4;
415     if (attrsize > MAX_EA_SIZE)
416         /* we arbitrarily make this fatal */
417         return AFPERR_PARAM;
418
419     LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
420
421     ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
422
423     return ret;
424 }
425
426 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
427 {
428     int                 oflag = O_RDONLY, ret;
429     uint16_t            vid, bitmap;
430     uint32_t            did, attrnamelen;
431     char                attrmname[256], attruname[256];
432     struct vol          *vol;
433     struct dir          *dir;
434     struct path         *s_path;
435
436     *rbuflen = 0;
437     ibuf += 2;
438
439     memcpy( &vid, ibuf, 2);
440     ibuf += 2;
441     if (NULL == ( vol = getvolbyvid( vid )) ) {
442         LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
443         return AFPERR_ACCESS;
444     }
445
446     memcpy( &did, ibuf, 4);
447     ibuf += 4;
448     if (NULL == ( dir = dirlookup( vol, did )) ) {
449         LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
450         return afp_errno;
451     }
452
453     memcpy( &bitmap, ibuf, 2);
454     bitmap = ntohs( bitmap );
455     ibuf += 2;
456
457 #ifdef HAVE_SOLARIS_EAS
458     if (bitmap & kXAttrNoFollow)
459         oflag |= AT_SYMLINK_NOFOLLOW;
460 #endif
461
462     /* get name */
463     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
464         LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
465         return AFPERR_NOOBJ;
466     }
467
468     if ((unsigned long)ibuf & 1)
469         ibuf++;
470
471     /* get length of EA name */
472     memcpy(&attrnamelen, ibuf, 2);
473     attrnamelen = ntohs(attrnamelen);
474     ibuf += 2;
475     if (attrnamelen > 255)
476         return AFPERR_PARAM;
477
478     /* we must copy the name as its not 0-terminated and we cant write to ibuf */
479     strncpy(attrmname, ibuf, attrnamelen);
480     attrmname[attrnamelen] = 0;
481     ibuf += attrnamelen;
482
483     /* Convert EA name in utf8 to unix charset */
484     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
485         return AFPERR_MISC;
486
487     if (attrnamelen == 255)
488         /* convert_string didn't 0-terminate */
489         attruname[255] = 0;
490
491     LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
492
493     ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);
494
495     return ret;
496 }
497