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