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