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