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